C++ Templates

【C++ Templates(18)】基于类型属性的重载

2018-06-16  本文已影响35人  downdemo
template<typename Number> void f(Number); // only for numbers
template<typename Container> void f(Container);// only for containers

算法特化

template<typename T>
void swap(T& x, T& y)
{
    T tmp(x);
    x = y;
    y = tmp;
}
template<typename T>
void swap(Array<T>& x, Array<T>& y)
{
    swap(x.ptr, y.ptr);
    swap(x.len, y.len);
}
template<typename InputIterator, typename Distance>
void advanceIter(InputIterator& x, Distance n)
{
    while (n > 0) { // linear time
        ++x;
        --n;
    }
}
template<typename RandomAccessIterator, typename Distance>
void advanceIter(RandomAccessIterator& x, Distance n) {
    x += n; // constant time
}

标签调度(Tag Dispatching)

template<typename Iterator, typename Distance>
void advanceIterImpl(Iterator& x, Distance n, std::input_iterator_tag)
{
    while (n > 0) { // linear time
        ++x;
        --n;
    }
}
template<typename Iterator, typename Distance>
void advanceIterImpl(Iterator& x, Distance n,
    std::random_access_iterator_tag) {
    x += n; // constant time
}
template<typename Iterator, typename Distance>
void advanceIter(Iterator& x, Distance n)
{
    advanceIterImpl(x, n,
        typename
        std::iterator_traits<Iterator>::iterator_category());
}
namespace std {
    struct input_iterator_tag { };
    struct output_iterator_tag { };
    struct forward_iterator_tag : public input_iterator_tag { };
    struct bidirectional_iterator_tag : public forward_iterator_tag { };
    struct random_access_iterator_tag : public bidirectional_iterator_tag { };
}

启用/禁用函数模板

template<typename Iterator>
constexpr bool IsRandomAccessIterator =
    IsConvertible<
        typename std::iterator_traits<Iterator>::iterator_category,
        std::random_access_iterator_tag>;

template<typename Iterator, typename Distance>
EnableIf<IsRandomAccessIterator<Iterator>>
advanceIter(Iterator& x, Distance n) {
    x += n; // constant time
}
// typeoverload/enableif.hpp

template<bool, typename T = void>
struct EnableIfT {
};

template< typename T>
struct EnableIfT<true, T> {
    using Type = T;
};

template<bool Cond, typename T = void>
using EnableIf = typename EnableIfT<Cond, T>::Type;
template<typename Iterator, typename Distance>
EnableIf<!IsRandomAccessIterator<Iterator>>
advanceIter(Iterator& x, Distance n)
{
    while (n > 0) { // linear time
        ++x;
        --n;
    }
}

提供多重特化

// typeoverload/advance2.hpp

#include <iterator>
// implementation for random access iterators:
template<typename Iterator, typename Distance>
EnableIf<IsRandomAccessIterator<Iterator>>
advanceIter(Iterator& x, Distance n) {
    x += n; // constant time
}

template<typename Iterator>
constexpr bool IsBidirectionalIterator =
    IsConvertible<
    typename std::iterator_traits<Iterator>::iterator_category,
    std::bidirectional_iterator_tag>;

// implementation for bidirectional iterators:
template<typename Iterator, typename Distance>
EnableIf<IsBidirectionalIterator<Iterator> &&
    !IsRandomAccessIterator<Iterator>>
advanceIter(Iterator& x, Distance n) {
    if (n > 0) {
        for ( ; n > 0; ++x, --n) { // linear time
        }
    } else {
        for ( ; n < 0; --x, ++n) { //linear time
        }
    }
}

// implementation for all other iterators:
template<typename Iterator, typename Distance>
EnableIf<!IsBidirectionalIterator<Iterator>>
advanceIter(Iterator& x, Distance n) {
    if (n < 0) {
        throw "advanceIter(): invalid iterator category for negative n";
    }
    while (n > 0) { // linear time
        ++x;
        --n;
    }
}

EnableIf在哪运行

// typeoverload/container1.hpp

#include <iterator>
#include "enableif.hpp"
#include "isconvertible.hpp"

template<typename Iterator>
constexpr bool IsInputIterator =
    IsConvertible<
        typename std::iterator_traits<Iterator>::iterator_category,
        std::input_iterator_tag>;

template<typename T>
class Container {
public:
    // construct from an input iterator sequence:
    template<typename Iterator,
        typename = EnableIf<IsInputIterator<Iterator>>>
    Container(Iterator first, Iterator last);

    // convert to a container so long as the value types are convertible:
    template<typename U, typename = EnableIf<IsConvertible<T, U>>>
    operator Container<U>() const;
};
// construct from an input iterator sequence:
template<typename Iterator,
    typename = EnableIf<IsInputIterator<Iterator> &&
    !IsRandomAccessIterator<Iterator>>>
Container(Iterator first, Iterator last);

template<typename Iterator,
    typename = EnableIf<IsRandomAccessIterator<Iterator>>>
Container(Iterator first, Iterator last); // ERROR: 重复声明构造函数模板
// construct from an input iterator sequence:
template<typename Iterator,
    typename = EnableIf<IsInputIterator<Iterator> &&
    !IsRandomAccessIterator<Iterator>>>
Container(Iterator first, Iterator last);

template<typename Iterator,
    typename = EnableIf<IsRandomAccessIterator<Iterator>>,
    typename = int> // extra dummy parameter to enable both constructors
Container(Iterator first, Iterator last); // OK now

编译期if

// typeoverload/advance3.hpp

template<typename Iterator, typename Distance>
void advanceIter(Iterator& x, Distance n) {
    if constexpr(IsRandomAccessIterator<Iterator>) {
        // implementation for random access iterators:
        x += n; // constant time
    }

    else if constexpr(IsBidirectionalIterator<Iterator>) {
        // implementation for bidirectional iterators:
        if (n > 0) {
            for ( ; n > 0; ++x, --n) { // linear time for positive n
            }
        } else {
            for ( ; n < 0; --x, ++n) { // linear time for negative n
            }
        }
    }

    else {
        // implementation for all other iterators that are at least input iterators:
        if (n < 0) {
            throw "advanceIter(): invalid iterator category for negative n";
        }
        while (n > 0) { // linear time for positive n only
            ++x;
            --n;
        }
    }
}
template<typename T>
void f(T p) {
    if constexpr (condition<T>::value) {
        // do something here...
    }
    else {
        // not a T for which f() makes sense:
        static_assert(condition<T>::value, "can't call f() for such a T");
    }
}

Concepts

// typeoverload/container4.hpp

template<typename T>
class Container {
public:
    // construct from an input iterator sequence:
    template<typename Iterator>
    requires IsInputIterator<Iterator>
    Container(Iterator first, Iterator last);

    // construct from a random access iterator sequence:
    template<typename Iterator>
    requires IsRandomAccessIterator<Iterator>
    Container(Iterator first, Iterator last);

    // convert to a container so long as the value types are convertible:
    template<typename U>
    requires IsConvertible<T, U>
    operator Container<U>() const;
};
template<typename T>
class Container {
public:
    ...
    requires HasLess<T>
    void sort() {
        ...
    }
};

类特化

template<typename Key, typename Value>
class Dictionary {
private:
    vector<pair<Key const, Value>> data;
public:
    //subscripted access to the data:
    value& operator[](Key const& key)
    {
        // search for the element with this key:
        for (auto& element : data) {
            if (element.first == key){
                return element.second;
            }
        }
        // there is no element with this key; add one
        data.push_back(pair<Key const, Value>(key, Value()));
        return data.back().second;
    }
    ...
};

启用/禁用类模板

template<typename Key, typename Value, typename = void>
class Dictionary
{
    ... // vector implementation as above
};
template<typename Key, typename Value>
class Dictionary<Key, Value,
    EnableIf<HasLess<Key>>>
{
private:
    map<Key, Value> data;
public:
    value& operator[](Key const& key) {
        return data[key];
    }
    ...
};
template<typename Key, typename Value, typename = void>
class Dictionary
{
    ... // vector implementation as above
};

template<typename Key, typename Value>
class Dictionary<Key, Value,
    EnableIf HasLess Key> && !HasHash Key>>> {
{
    ... // map implementation as above
};

template typename Key, typename Value>
class Dictionary Key, Value,
    EnableIf HasHash Key>>>
{
private:
    unordered_map Key, Value> data;
public:
    value& operator[](Key const& key) {
        return data[key];
    }
    ...
};

用于类模板的Tag Dispatching

// primary template (intentionally undefined):
template<typename Iterator,
    typename Tag =
        BestMatchInSet<
            typename std::iterator_traits<Iterator>::iterator_category,
            std::input_iterator_tag,
            std::bidirectional_iterator_tag,
            std::random_access_iterator_tag>>
class Advance;

// general, linear-time implementation for input iterators:
template<typename Iterator>
class Advance<Iterator, std::input_iterator_tag>
{
public:
    using DifferenceType =
        typename std::iterator_traits<Iterator>::difference_type;
    void operator() (Iterator& x, DifferenceType n) const
    {
        while (n > 0) {
            ++x;
            --n;
        }
    }
};

// bidirectional, linear-time algorithm for bidirectional iterators:
template<typename Iterator>
class Advance<Iterator, std::bidirectional_iterator_tag>
{
public:
    using DifferenceType =
        typename std::iterator_traits<Iterator>::difference_type;
    void operator() (Iterator& x, DifferenceType n) const
    {
        if (n > 0) {
            while (n > 0) {
                ++x;
                --n;
            }
        } else {
            while (n < 0) {
                --x;
                ++n;
            }
        }
    }
};

// bidirectional, constant-time algorithm for random access iterators:
template<typename Iterator>
class Advance<Iterator, std::random_access_iterator_tag>
{
public:
    using DifferenceType =
        typename std::iterator_traits<Iterator>::difference_type;
    void operator() (Iterator& x, DifferenceType n) const
    {
        x += n;
    }
};
void f(std::input_iterator_tag);
void f(std::bidirectional_iterator_tag);
void f(std::random_access_iterator_tag);
// construct a set of match() overloads for the types in Types...:
template<typename... Types>
struct MatchOverloads;

// basis case: nothing matched:
template<>
struct MatchOverloads<> {
    static void match(...);
};

// recursive case: introduce a new match() overload:
template<typename T1, typename... Rest>
struct MatchOverloads<T1, Rest...> : public MatchOverloads<Rest...>
{
    static T1 match(T1); // introduce overload for T1
    using MatchOverloads<Rest...>::match; // collect overloads from bases
};

// find the best match for T in Types...:
template<typename T, typename... Types>
struct BestMatchInSetT {
    using Type = decltype(MatchOverloads<Types...>::match(declval<T>()));
};

template<typename T, typename... Types>
using BestMatchInSet = typename BestMatchInSetT<T, Types...>::Type;

实例化安全的(instantiation-safe)模板

template<typename T>
T const& min(T const& x, T const& y)
{
    if (y < x) {
        return y;
    }
    return x;
}
// typeoverload/lessresult.hpp

#include <utility> // for declval()
#include <type_traits> // for true_type and false_type

template<typename T1, typename T2>
class HasLess {
    template<typename T> struct Identity;
    template<typename U1, typename U2> static std::true_type
        test(Identity<decltype(std::declval<U1>() < std::declval<U2>())>*);
    template<typename U1, typename U2> static std::false_type
        test(...);
public:
    static constexpr bool value = decltype(test<T1, T2>(nullptr))::value;
};

template<typename T1, typename T2, bool HasLess>
class LessResultImpl {
public:
    using Type = decltype(std::declval<T1>() < std::declval<T2>());
};

template<typename T1, typename T2>
class LessResultImpl<T1, T2, false> {
};

template<typename T1, typename T2>
class LessResultT
: public LessResultImpl<T1, T2, HasLess<T1, T2>::value> {
};

template<typename T1, typename T2>
using LessResult = typename LessResultT<T1, T2>::Type;
// typeoverload/min2.hpp

#include "isconvertible.hpp"
#include "lessresult.hpp"

template<typename T>
EnableIf<IsConvertible<LessResult<T const&, T const&>, bool>,
    T const&>
min(T const& x, T const& y)
{
    if (y < x) {
        return y;
    }
    return x;
}
// typeoverload/min.cpp

#include"min.hpp"

struct X1 { };
bool operator< (X1 const&, X1 const&) { return true; }

struct X2 { };
bool operator<(X2, X2) { return true; }

struct X3 { };
bool operator<(X3&, X3&) { return true; }

struct X4 { };

struct BoolConvertible {
    operator bool() const { return true; } // implicit conversion to bool
};
struct X5 { };
BoolConvertible operator< (X5 const&, X5 const&)
{
    return BoolConvertible();
}

struct NotBoolConvertible { // no conversion to bool
};
struct X6 { };
NotBoolConvertible operator< (X6 const&, X6 const&)
{
    return NotBoolConvertible();
}

struct BoolLike {
    explicit operator bool() const { return true; } // explicit conversion to bool
};
struct X7 { };
BoolLike operator< (X7 const&, X7 const&) { return BoolLike(); }

int main()
{
    min(X1(), X1()); // X1 can be passed to min()
    min(X2(), X2()); // X2 can be passed to min()
    min(X3(), X3()); // ERROR: X3 cannot be passed to min()
    min(X4(), X4()); // ERROR: X4 can be passed to min()
    min(X5(), X5()); // X5 can be passed to min()
    min(X6(), X6()); // ERROR: X6 cannot be passed to min()
    min(X7(), X7()); // UNEXPECTED ERROR: X7 cannot be passed to min()
}
// typeoverload/iscontextualbool.hpp

#include <utility> // for declval()
#include <type_traits> // for true_type and false_type

template<typename T>
class IsContextualBoolT {
private:
    template<typename T> struct Identity;
    template<typename U> static std::true_type
        test(Identity<decltype(declval<U>()? 0 : 1)>*);
    template<typename U> static std::false_type
        test(...);
public:
    static constexpr bool value = decltype(test<T>(nullptr))::value;
};

template<typename T>
constexpr bool IsContextualBool = IsContextualBoolT<T>::value;
// typeoverload/min3.hpp

#include "iscontextualbool.hpp"
#include "lessresult.hpp"

template<typename T>
EnableIf<IsContextualBool<LessResult<T const&, T const&>>, T const&>
min(T const& x, T const& y)
{
    if (y < x) {
        return y;
    }
    return x;
}

在标准库中

上一篇下一篇

猜你喜欢

热点阅读