C++ Templates

【C++ Templates(23)】Tuple

2018-07-02  本文已影响26人  downdemo
template<typename... Types>
class Tuple {
    ... // implementation discussed below
};
Tuple<int, double, std::string> t(17, 3.14, "Hello, World!");

基本Tuple设计

存储

template<typename... Types>
class Tuple;

// recursive case:
template<typename Head, typename... Tail>
class Tuple<Head, Tail...>
{
private:
    Head head;
    Tuple<Tail...> tail;

public:
    // constructors:
    Tuple() {
    }
    Tuple(Head const& head, Tuple<Tail...> const& tail)
    : head(head), tail(tail) {
    }
...
    Head& getHead() { return head; }
    Head const& getHead() const { return head; }
    Tuple<Tail...>& getTail() { return tail; }
    Tuple<Tail...> const& getTail() const { return tail; }
};

// basis case:
template<>
class Tuple<> {
    // no storage required
};
// tuples/tupleget.hpp

// recursive case:
template<unsigned N>
struct TupleGet {
    template<typename Head, typename... Tail>
    static auto apply(Tuple<Head, Tail...> const& t) {
        return TupleGet<N-1>::apply(t.getTail());
    }
};

// basis case:
template<>
struct TupleGet<0> {
    template<typename Head, typename... Tail>
    static Head const& apply(Tuple<Head, Tail...> const& t) {
        return t.getHead();
    }
};

template<unsigned N, typename... Types>
auto get(Tuple<Types...> const& t) {
    return TupleGet<N>::apply(t);
}

构造

Tuple() {
} Tuple(Head const& head, Tuple<Tail...> const& tail)
: head(head), tail(tail) {
}
Tuple(Head const& head, Tail const&... tail)
: head(head), tail(tail...) {
}
template<typename VHead, typename... VTail>
Tuple(VHead&& vhead, VTail&&... vtail)
: head(std::forward<VHead>(vhead)),
    tail(std::forward<VTail>(vtail)...) {
}
template<typename VHead, typename... VTail>
Tuple(Tuple<VHead, VTail...> const& other)
: head(other.getHead()), tail(other.getTail()) { }
// ERROR: no conversion from Tuple<int, double, string> to long
Tuple<long int, long double, std::string> t2(t);
template<typename VHead, typename... VTail,
    typename = std::enable_if_t<sizeof...(VTail)==sizeof...(Tail)>>
Tuple(VHead&& vhead, VTail&&... vtail)
  : head(std::forward<VHead>(vhead)),
    tail(std::forward<VTail>(vtail)...) { }
template<typename VHead, typename... VTail,
    typename = std::enable_if_t<sizeof...(VTail)==sizeof...(Tail)>>
Tuple(Tuple<VHead, VTail...> const& other)
  : head(other.getHead()), tail(other.getTail()) { }
// tuples/maketuple.hpp
template<typename... Types>
auto makeTuple(Types&&... elems)
{
    return Tuple<std::decay_t<Types>...>(std::forward<Types>(elems)...);
}
makeTuple(17, 3.14, "Hello, World!")
Tuple<int, double, char const*>

基本Tuple操作

比较

// tuples/tupleeq.hpp

// basis case:
bool operator==(Tuple<> const&, Tuple<> const&)
{
    // empty tuples are always equivalent
    return true;
}

// recursive case:
template<typename Head1, typename... Tail1,
    typename Head2, typename... Tail2,
    typename = std::enable_if_t<sizeof...(Tail1)==sizeof...(Tail2)>>
bool operator==(Tuple<Head1, Tail1...> const& lhs,
    Tuple<Head2, Tail2...> const& rhs)
{
    return lhs.getHead() == rhs.getHead() &&
        lhs.getTail() == rhs.getTail();
}

输出

// tuples/tupleio.hpp

#include <iostream>
void printTuple(std::ostream& strm, Tuple<> const&,
    bool isFirst = true)
{
    strm << ( isFirst ? '(' : ')' );
}

template<typename Head, typename... Tail>
void printTuple(std::ostream& strm, Tuple<Head, Tail...> const& t,
    bool isFirst = true)
{
    strm << ( isFirst ? "(" : ", " );
    strm << t.getHead();
    printTuple(strm, t.getTail(), false);
}

template<typename... Types>
std::ostream& operator<<(std::ostream& strm, Tuple<Types...> const& t)
{
    printTuple(strm, t);
    return strm;
}
std::cout << makeTuple(1, 2.5, std::string("hello")) << '\n';
// prints
(1, 2.5, hello)

Tuple算法

Tuple作为Typelist

// tuples/tupletypelist.hpp

// determine whether the tuple is empty:
template<>
struct IsEmpty<Tuple<>> {
    static constexpr bool value = true;
};

// extract front element:
template<typename Head, typename... Tail>
class FrontT<Tuple<Head, Tail...>> {
public:
    using Type = Head;
};

// remove front element:
template<typename Head, typename... Tail>
class PopFrontT<Tuple<Head, Tail...>> {
public:
    using Type = Tuple<Tail...>;
};

// add element to the front:
template<typename... Types, typename Element>
class PushFrontT<Tuple<Types...>, Element> {
public:
    using Type = Tuple<Element, Types...>;
};

// add element to the back:
template<typename... Types, typename Element>
class PushBackT<Tuple<Types...>, Element> {
public:
    using Type = Tuple<Types..., Element>;
};
Tuple<int, double, std::string> t1(17, 3.14, "Hello, World!");
using T2 = PopFront<PushBack<decltype(t1), bool>>;
T2 t2(get<1>(t1), get<2>(t1), true);
std::cout << t2; // 打印(3.14, Hello, World!, 1)

从Tuple添加和移除

// tuples/pushfront.hpp

template<typename... Types, typename V>
PushFront<Tuple<Types...>, V>
pushFront(Tuple<Types...> const& tuple, V const& value)
{
    return PushFront<Tuple<Types...>, V>(value, tuple);
}
// tuples/pushback.hpp

// basis case
template<typename V>
Tuple<V> pushBack(Tuple<> const&, V const& value)
{
    return Tuple<V>(value);
}

// recursive case
template<typename Head, typename... Tail, typename V>
Tuple<Head, Tail..., V>
pushBack(Tuple<Head, Tail...> const& tuple, V const& value)
{
    return Tuple<Head, Tail..., V>(tuple.getHead(),
        pushBack(tuple.getTail(), value));
}
// tuples/popfront.hpp

template<typename... Types>
PopFront<Tuple<Types...>>
popFront(Tuple<Types...> const& tuple)
{
    return tuple.getTail();
}
Tuple<int, double, std::string> t1(17, 3.14, "Hello, World!");
auto t2 = popFront(pushBack(t1, true));
std::cout << std::boolalpha << t2 << '\n'; // 打印(3.14, Hello, World!, true)

翻转Tuple

// tuples/reverse.hpp

// basis case
Tuple<> reverse(Tuple<> const& t)
{
    return t;
}

// recursive case
template<typename Head, typename... Tail>
Reverse<Tuple<Head, Tail...>> reverse(Tuple<Head, Tail...> const& t)
{
    return pushBack(reverse(t.getTail()), t.getHead());
}
// tuples/popback.hpp

template<typename... Types>
PopBack<Tuple<Types...>>
popBack(Tuple<Types...> const& tuple)
{
    return reverse(popFront(reverse(tuple)));
}

索引表

// tuples/copycounter.hpp
template<int N>
struct CopyCounter
{
    inline static unsigned numCopies = 0;
    CopyCounter() {
    }
    CopyCounter(CopyCounter const&) {
        ++numCopies;
    }
};
// tuples/copycountertest.hpp
void copycountertest()
{
    Tuple<CopyCounter<0>, CopyCounter<1>, CopyCounter<2>,
        CopyCounter<3>, CopyCounter<4>> copies;
    auto reversed = reverse(copies);
    std::cout << "0: " << CopyCounter<0>::numCopies << " copies\n";
    std::cout << "1: " << CopyCounter<1>::numCopies << " copies\n";
    std::cout << "2: " << CopyCounter<2>::numCopies << " copies\n";
    std::cout << "3: " << CopyCounter<3>::numCopies << " copies\n";
    std::cout << "4: " << CopyCounter<4>::numCopies << " copies\n";
}
0: 5 copies
1: 8 copies
2: 9 copies
3: 8 copies
4: 5 copies
auto reversed = makeTuple(get<4>(copies), get<3>(copies),
    get<2>(copies), get<1>(copies), get<0>(copies));
0: 1 copies
1: 1 copies
2: 1 copies
3: 1 copies
4: 1 copies

使用索引表翻转

Valuelist<unsigned, 4, 3, 2, 1, 0>
// tuples/makeindexlist.hpp

// recursive case
template<unsigned N, typename Result = Valuelist<unsigned>>
struct MakeIndexListT
: MakeIndexListT<N-1, PushFront<Result, CTValue<unsigned, N-1>>>
{
};

// basis case
template<typename Result>
struct MakeIndexListT<0, Result>
{
    using Type = Result;
};
template<unsigned N>
using MakeIndexList = typename MakeIndexListT<N>::Type;
using MyIndexList = Reverse<MakeIndexList<5>>;
// equivalent to Valuelist<unsigned, 4, 3, 2, 1, 0>
// tuples/indexlistreverse.hpp

template<typename... Elements, unsigned... Indices>
auto reverseImpl(Tuple<Elements...> const& t,
    Valuelist<unsigned, Indices...>)
{
    return makeTuple(get<Indices>(t)...);
}

template<typename... Elements>
auto reverse(Tuple<Elements...> const& t)
{
    return reverseImpl(t,
        Reverse<MakeIndexList<sizeof...(Elements)>>());
}
-> decltype(makeTuple(get<Indices>(t)...))
// 和
-> decltype(reverseImpl(t, Reverse<MakeIndexList<sizeof...(Elements)>>()))

洗牌算法和选择算法(Shuffle and Select)

// tuples/select.hpp

template<typename... Elements, unsigned... Indices>
auto select(Tuple<Elements...> const& t, Valuelist<unsigned, Indices...>)
{
    return makeTuple(get<Indices>(t)...);
}
Tuple<int, double, std::string> t1(42, 7.7, "hello"};
auto a = splat<1, 4>(t);
std::cout << a << '\n'; // (7.7, 7.7, 7.7, 7.7)
// tuples/splat.hpp

template<unsigned I, unsigned N, typename IndexList = Valuelist<unsigned>>
class ReplicatedIndexListT;

template<unsigned I, unsigned N, unsigned... Indices>
class ReplicatedIndexListT<I, N, Valuelist<unsigned, Indices...>>
  : public ReplicatedIndexListT<I, N-1, Valuelist<unsigned, Indices..., I>> {
};

template<unsigned I, unsigned... Indices>
class ReplicatedIndexListT<I, 0, Valuelist<unsigned, Indices...>> {
public:
    using Type = Valuelist<unsigned, Indices...>;
};

template<unsigned I, unsigned N>
using ReplicatedIndexList = typename ReplicatedIndexListT<I, N>::Type;

template<unsigned I, unsigned N, typename... Elements>
auto splat(Tuple<Elements...> const& t)
{
    return select(t, ReplicatedIndexList<I, N>());
}
// tuples/tuplesorttest.hpp

#include <complex>
template<typename T, typename U>
class SmallerThanT
{
public:
    static constexpr bool value = sizeof(T) < sizeof(U);
};

void testTupleSort()
{
    auto T1 = makeTuple(17LL, std::complex<double>(42,77), 'c', 42, 7.7);
    std::cout << t1 << '\n';
    auto T2 = sort<SmallerThanT>(t1); // t2 is Tuple<int, long, std::string>
    std::cout << "sorted by size: " << t2 << '\n';
}
(17, (42,77), c, 42, 7.7)
sorted by size: (c, 42, 7.7, 17, (42,77))
// tuples/tuplesort.hpp

// metafunction wrapper that compares the elements in a tuple:
template<typename List, template<typename T, typename U> class F>
class MetafunOfNthElementT {
public:
    template<typename T, typename U> class Apply;

    template<unsigned N, unsigned M>
    class Apply<CTValue<unsigned, M>, CTValue<unsigned, N>>
      : public F<NthElement<List, M>, NthElement<List, N>> { };
};

// sort a tuple based on comparing the element types:
template<template<typename T, typename U> class Compare,
    typename... Elements>
auto sort(Tuple<Elements...> const& t)
{
    return select(t,
        InsertionSort<MakeIndexList<sizeof...(Elements)>,
            MetafunOfNthElementT<
                Tuple<Elements...>,
                Compare>::template Apply>());
}
// tuples/indexsort.cpp

#include <vector>
#include <algorithm>
#include <string>
int main()
{
    std::vector<std::string> strings = {"banana", "apple", "cherry"};
    std::vector<unsigned> indices = { 0, 1, 2 };
    std::sort(indices.begin(), indices.end(),
        [&strings](unsigned i, unsigned j) {
            return strings[i] < strings[j];
        });
}

扩展Tuple

Tuple<std::string, char const*, int, char> t("Pi", "is roughly", 3, '\n');
print(t...); //ERROR: cannot expand a tuple; it isn't a parameter pack
// tuples/apply.hpp

template<typename F, typename... Elements, unsigned... Indices>
auto applyImpl(F f, Tuple<Elements...> const& t,
    Valuelist<unsigned, Indices...>)
    ->decltype(f(get<Indices>(t)...))
{
    return f(get<Indices>(t)...);
}

template<typename F, typename... Elements,
    unsigned N = sizeof...(Elements)>
auto apply(F f, Tuple<Elements...> const& t)
    ->decltype(applyImpl(f, t, MakeIndexList<N>()))
{
    return applyImpl(f, t, MakeIndexList<N>());
}
Tuple<std::string, char const*, int, char> t("Pi", "is roughly", 3, '\n');
apply(print, t); // OK: prints Pi is roughly 3

优化Tuple

Tuple与EBCO

// tuples/tuplestorage1.hpp

// recursive case:
template<typename Head, typename... Tail>
class Tuple<Head, Tail...> : private Tuple<Tail...>
{
private:
    Head head;
public:
    Head& getHead() { return head; }
    Head const& getHead() const { return head; }
    Tuple<Tail...>& getTail() { return *this; }
    Tuple<Tail...> const& getTail() const { return *this; }
};
// tuples/tuplestorage2.hpp

template<typename... Types>
class Tuple;

template<typename T>
class TupleElt
{
    T value;

public:
    TupleElt() = default;

    template<typename U>
    TupleElt(U&& other) : value(std::forward<U>(other) { }

    T& get() { return value; }
    T const& get() const { return value; }
};

// recursive case:
template<typename Head, typename... Tail>
class Tuple<Head, Tail...>
  : private TupleElt<Head>, private Tuple<Tail...>
{
public:
    Head& getHead() {
        // potentially ambiguous
        return static_cast<TupleElt<Head> *>(this)->get();
    }
    Head const& getHead() const {
        // potentially ambiguous
        return static_cast<TupleElt<Head> const*>(this)->get();
    }
    Tuple<Tail...>& getTail() { return *this; }
    Tuple<Tail...> const& getTail() const { return *this; }
};

// basis case:
template<>
class Tuple<> {
    // no storage required
};
// tuples/tupleelt1.hpp

template<unsigned Height, typename T>
class TupleElt {
    T value;
public:
    TupleElt() = default;
    template<typename U>
    TupleElt(U&& other) : value(std::forward<U>(other)) { }
    T& get() { return value; }
    T const& get() const { return value; }
};
// tuples/tuplestorage3.hpp

template<typename... Types>
class Tuple;

// recursive case:
template<typename Head, typename... Tail>
class Tuple<Head, Tail...>
  : private TupleElt<sizeof...(Tail), Head>, private Tuple<Tail...>
{
    using HeadElt = TupleElt<sizeof...(Tail), Head>;
public:
    Head& getHead() {
        return static_cast<HeadElt *>(this)->get();
    }
    Head const& getHead() const {
        return static_cast<HeadElt const*>(this)->get();
    }
    Tuple<Tail...>& getTail() { return *this; }
    Tuple<Tail...> const& getTail() const { return *this; }
};

// basis case:
template<>
class Tuple<> {
    // no storage required
};
// tuples/compressedtuple1.cpp

#include <algorithm>
#include "tupleelt1.hpp"
#include "tuplestorage3.hpp"
#include <iostream>

struct A {
    A() {
        std::cout << "A()" << '\n';
    }
};

struct B {
    B() {
        std::cout << "B()" << '\n';
    }
};

int main()
{
    Tuple<A, char, A, char, B> t1;
    std::cout << sizeof(t1) << " bytes" << '\n';
}
A()
A()
B()
5 bytes
// tuples/tupleelt2.hpp

#include <type_traits>

template<unsigned Height, typename T,
    bool = std::is_class<T>::value && !std::is_final<T>::value>
class TupleElt;

template<unsigned Height, typename T>
class TupleElt<Height, T, false>
{
    T value;

public:
    TupleElt() = default;
    template<typename U>
    TupleElt(U&& other) : value(std::forward<U>(other)) { }

    T& get() { return value; }
    T const& get() const { return value; }
};

template<unsigned Height, typename T>
class TupleElt<Height, T, true> : private T
{
public:
    TupleElt() = default;
    template<typename U>
    TupleElt(U&& other) : T(std::forward<U>(other)) { }

    T& get() { return *this; }
    T const& get() const { return *this; }
};
A()
A()
B()
2 bytes

常数时间的get()

// tuples/constantget.hpp

template<unsigned H, typename T>
T& getHeight(TupleElt<H,T>& te)
{
    return te.get();
}

template<typename... Types>
class Tuple;

template<unsigned I, typename... Elements>
auto get(Tuple<Elements...>& t)
    -> decltype(getHeight<sizeof...(Elements)-I-1>(t))
{
    return getHeight<sizeof...(Elements)-I-1>(t);
}
// inside the recursive case for class template Tuple:
template<unsigned I, typename... Elements>
friend auto get(Tuple<Elements...>& t)
    -> decltype(getHeight<sizeof...(Elements)-I-1>(t));

Tuple下标(Subscript)

template<typename T, T Index>
auto& operator[](CTValue<T, Index>) {
    return get<Index>(*this);
}
auto t = makeTuple(0, '1', 2.2f, std::string{"hello"});
auto a = t[CTValue<unsigned, 2>{}];
auto b = t[CTValue<unsigned, 3>{}];
// tuples/literals.hpp

#include "ctvalue.hpp"
#include <cassert>
#include <cstddef>

// convert single char to corresponding int value at compile time:
constexpr int toInt(char c) {
    // hexadecimal letters:
    if (c >= 'A' && c <= 'F') {
        return static_cast<int>(c) - static_cast<int>('A') + 10;
    }
    if (c >= 'a' && c <= 'f') {
        return static_cast<int>(c) - static_cast<int>('a') + 10;
    }
    // other (disable '.' for floating-point literals):
    assert(c >= '0' && c <= '9');
    return static_cast<int>(c) - static_cast<int>('0');
}

// parse array of chars to corresponding int value at compile time:
template<std::size_t N>
constexpr int parseInt(char const (&arr)[N]) {
    int base = 10; // to handle base (default: decimal)
    int offset = 0; // to skip prefixes like 0x
    if (N > 2 && arr[0] == '0') {
        switch (arr[1]) {
            case 'x': //prefix 0x or 0X, so hexadecimal
            case 'X':
                base = 16;
                offset = 2;
                break;
            case 'b': //prefix 0b or 0B (since C++14), so binary
            case 'B':
                base = 2;
                offset = 2;
                break;
            default: // prefix 0, so octal
                base = 8;
                offset = 1;
                break;
    }
}

    // iterate over all digits and compute resulting value:
    int value = 0;
    int multiplier = 1;
    for (std::size_t i = 0; i < N - offset; ++i) {
        if (arr[N-1-i] != '\'') { // ignore separating single quotes (e.g. in 1'000)
            value += toInt(arr[N-1-i]) * multiplier;
            multiplier *= base;
        }
    }
    return value;
}

// literal operator: parse integral literals with suffix _c as sequence of chars:
template<char... cs>
constexpr auto operator"" _c() {
    return CTValue<int, parseInt<sizeof...(cs)>({cs...})>{};
}
auto t = makeTuple(0, '1', 2.2f, std::string{"hello"});
auto c = t[2_c];
auto d = t[3_c];
上一篇下一篇

猜你喜欢

热点阅读