C++ Templates

【C++ Templates(24)】Discriminated

2018-07-05  本文已影响118人  downdemo
Variant<int, double, string> field;
// variant/variant.cpp

#include "variant.hpp"
#include <iostream>
#include <string>
int main()
{
    Variant<int, double, std::string> field(17);
    if (field.is<int>()) {
        std::cout << "Field stores the integer "
            << field.get<int>() << '\n';
    }
    field = 42; // assign value of same type
    field = "hello"; // assign value of different type
    std::cout << "Field now stores the string '"
        << field.get<std::string>() << "'\n";
}
Field stores the integer 17
Field now stores the string "hello"

存储

// variant/variantstorageastuple.hpp
template<typename... Types>
class Variant {
public:
    Tuple<Types...> storage;
    unsigned char discriminator;
};
// variant/variantstorageasunion.hpp

template<typename... Types>
union VariantStorage;

template<typename Head, typename... Tail>
union VariantStorage<Head, Tail...> {
    Head head;
    VariantStorage<Tail...> tail;
};

template<>
union VariantStorage<> {
};
// variant/variantstorage.hpp

#include <new> // for std::launder()
template<typename... Types>
class VariantStorage {
    using LargestT = LargestType<Typelist<Types...>>;
    alignas(Types...) unsigned char buffer[sizeof(LargestT)];
    unsigned char discriminator = 0;
public:
    unsigned char getDiscriminator() const { return discriminator; }
    void setDiscriminator(unsigned char d) { discriminator = d; }
    void* getRawBuffer() { return buffer; }
    const void* getRawBuffer() const { return buffer; }
    template<typename T>
    T* getBufferAs() { return std::launder(reinterpret_cast<T*>(buffer)); }
    template<typename T>
    T const* getBufferAs() const {
        return std::launder(reinterpret_cast<T const*>(buffer));
    }
};

设计

// variant/variantchoice.hpp

#include "findindexof.hpp"

template<typename T, typename... Types>
class VariantChoice {
    using Derived = Variant<Types...>;
    Derived& getDerived() { return *static_cast<Derived*>(this); }
    Derived const& getDerived() const {
        return *static_cast<Derived const*>(this);
    }
protected:
    // compute the discriminator to be used for this type
    constexpr static unsigned Discriminator =
        FindIndexOfT<Typelist<Types...>, T>::value + 1;
public:
    VariantChoice() { }
    VariantChoice(T const& value); // see variantchoiceinit.hpp
    VariantChoice(T&& value); // see variantchoiceinit.hpp
    bool destroy(); // see variantchoicedestroy.hpp
    Derived& operator= (T const& value); // see variantchoiceassign.hpp
    Derived& operator= (T&& value); // see variantchoiceassign.hpp
};
// variant/findindexof.hpp

template<typename List, typename T, unsigned N = 0,
bool Empty = IsEmpty<List>::value>
struct FindIndexOfT;

// recursive case:
template<typename List, typename T, unsigned N>
struct FindIndexOfT<List, T, N, false>
  : public IfThenElse<std::is_same<Front<List>, T>::value,
    std::integral_constant<unsigned, N>,
    FindIndexOfT<PopFront<List>, T, N+1>>
{
};

// basis case:
template<typename List, typename T, unsigned N>
struct FindIndexOfT<List, T, N, true>
{
};
// variant/variant-skel.hpp

template<typename... Types>
class Variant
  : private VariantStorage<Types...>,
    private VariantChoice<Types, Types...>...
{
    template<typename T, typename... OtherTypes>
    friend class VariantChoice; // enable CRTP
    ...
};
VariantChoice<Types, Types...>...
Variant<int, double, std::string>
VariantChoice<int, int, double, std::string>,
VariantChoice<double, int, double, std::string>,
VariantChoice<std::string, int, double, std::string>
// variant/variant.hpp

template<typename... Types>
class Variant
  : private VariantStorage<Types...>,
    private VariantChoice<Types, Types...>...
{
    template<typename T, typename... OtherTypes>
    friend class VariantChoice;
public:
    template<typename T> bool is() const; // see variantis.hpp
    template<typename T> T& get() &; // see variantget.hpp
    template<typename T> T const& get() const&; // see variantget.hpp
    template<typename T> T&& get() &&; // see variantget.hpp

    // see variantvisit.hpp:
    template<typename R = ComputedResultType, typename Visitor>
        VisitResult<R, Visitor, Types&...> visit(Visitor&& vis) &;
    template<typename R = ComputedResultType, typename Visitor>
        VisitResult<R, Visitor, Types const&...> visit(Visitor&& vis) const&;
    template<typename R = ComputedResultType, typename Visitor>
        VisitResult<R, Visitor, Types&&...> visit(Visitor&& vis) &&;

    using VariantChoice<Types, Types...>::VariantChoice...;
    Variant(); // see variantdefaultctor.hpp
    Variant(Variant const& source); // see variantcopyctor.hpp
    Variant(Variant&& source); // see variantmovector.hpp
    template<typename... SourceTypes>
        Variant(Variant<SourceTypes...> const& source); // variantcopyctortmpl.hpp
    template<typename... SourceTypes>
        Variant(Variant<SourceTypes...>&& source);

    using VariantChoice<Types, Types...>::operator=...;
    Variant& operator= (Variant const& source); // see variantcopyassign.hpp
    Variant& operator= (Variant&& source);
    template<typename... SourceTypes>
        Variant& operator= (Variant<SourceTypes...> const& source);
    template<typename... SourceTypes>
        Variant& operator= (Variant<SourceTypes...>&& source);

    bool empty() const;

    ~Variant() { destroy(); }
    void destroy(); // see variantdestroy.hpp
};

值查询与提取

// variant/variantis.hpp

template<typename... Types>
template<typename T>
bool Variant<Types...>::is() const
{
    return this->getDiscriminator() ==
        VariantChoice<T, Types...>::Discriminator;
}
// variant/variantget.hpp

#include <exception>
class EmptyVariant : public std::exception {
};

template<typename... Types>
    template<typename T>
T& Variant<Types...>::get() & {
    if (empty()) {
        throw EmptyVariant();
    }
    assert(is<T>());
    return *this->template getBufferAs<T>();
}

元素初始化、赋值和析构

初始化

// variant/variantchoiceinit.hpp

#include <utility> // for std::move()
template<typename T, typename... Types>
VariantChoice<T, Types...>::VariantChoice(T const& value) {
    // place value in buffer and set type discriminator:
    new(getDerived().getRawBuffer()) T(value);
    getDerived().setDiscriminator(Discriminator);
}

template<typename T, typename... Types>
VariantChoice<T, Types...>::VariantChoice(T&& value) {
    // place moved value in buffer and set type discriminator:
    new(getDerived().getRawBuffer()) T(std::move(value));
    getDerived().setDiscriminator(Discriminator);
}
Variant<int, double, string> v("hello"); // implicitly converted to string
using VariantChoice<Types, Types...>::VariantChoice...;
Variant(int const&);
Variant(int&&);
Variant(double const&);
Variant(double&&);
Variant(string const&);
Variant(string&&);

析构

// variant/variantchoicedestroy.hpp

template<typename T, typename... Types>
bool VariantChoice<T, Types...>::destroy() {
    if (getDerived().getDiscriminator() == Discriminator) {
        // if type matches, call placement delete:
        getDerived().template getBufferAs<T>()->~T();
        return true;
    }
    return false;
}
// variant/variantdestroy.hpp

template<typename... Types>
void Variant<Types...>::destroy() {
    // call destroy() on each VariantChoice base class; at most one will succeed:
    bool results[] = {
        VariantChoice<Types, Types...>::destroy()...
    };
    // indicate that the variant does not store a value
    this->setDiscriminator(0);
}
// variant/variantdestroy17.hpp

template<typename... Types>
void Variant<Types...>::destroy()
{
    // call destroy() on each VariantChoice base class; at most one will succeed:
    (VariantChoice<Types, Types...>::destroy() , ...);
    // indicate that the variant does not store a value
    this->setDiscriminator(0);
}

赋值

// variant/variantchoiceassign.hpp

template<typename T, typename... Types>
auto VariantChoice<T, Types...>::operator= (T const& value) ->
Derived& {
    if (getDerived().getDiscriminator() == Discriminator) {
        // assign new value of same type:
        *getDerived().template getBufferAs<T>() = value;
    }
    else {
        // assign new value of different type:
        getDerived().destroy(); // try destroy() for all types
        new(getDerived().getRawBuffer()) T(value); // place new value
        getDerived().setDiscriminator(Discriminator);
    }
    return getDerived();
}

template<typename T, typename... Types>
auto VariantChoice<T, Types...>::operator= (T&& value) -> Derived&
{
    if (getDerived().getDiscriminator() == Discriminator) {
        // assign new value of same type:
        *getDerived().template getBufferAs<T>() = std::move(value);
    }
    else {
        // assign new value of different type:
        getDerived().destroy(); // try destroy() for all types
        new(getDerived().getRawBuffer()) T(std::move(value)); // place new value
        getDerived().setDiscriminator(Discriminator);
    }
    return getDerived();
}
using VariantChoice<Types, Types...>::operator=...;

自赋值

v = v.get<T>()

异常

// variant/variantexception.cpp

#include "variant.hpp"
#include <exception>
#include <iostream>
#include <string>
class CopiedNonCopyable : public std::exception
{
};

class NonCopyable
{
public:
    NonCopyable() {
    }

    NonCopyable(NonCopyable const&) {
        throw CopiedNonCopyable();
    }

    NonCopyable(NonCopyable&&) = default;

    NonCopyable& operator= (NonCopyable const&) {
        throw CopiedNonCopyable();
    }

    NonCopyable& operator= (NonCopyable&&) = default;
};

int main()
{
    Variant<int, NonCopyable> v(17);
    try {
        NonCopyable nc;
        v = nc;
    }
    catch (CopiedNonCopyable) {
        std::cout << "Copy assignment of NonCopyable failed." << '\n';
        if (!v.is<int>() && !v.is<NonCopyable>()) {
            std::cout << "Variant has no value." << '\n';
        }
    }
}
Copy assignment of NonCopyable failed.
Variant has no value.
// variant/variantempty.hpp

template<typename... Types>
bool Variant<Types...>::empty() const {
    return this->getDiscriminator() == 0;
}

std::launder()

访问者(Visitor)

if (v.is<int>()) {
    std::cout << v.get<int>();
}
else if (v.is<double>()) {
    std::cout << v.get<double>();
}
else {
    std::cout << v.get<string>();
}
// variant/printrec.cpp

#include "variant.hpp"
#include <iostream>

template<typename V, typename Head, typename... Tail>
void printImpl(V const& v)
{
    if (v.template is<Head>()) {
        std::cout << v.template get<Head>();
    }
    else if constexpr (sizeof...(Tail) > 0) {
        printImpl<V, Tail...>(v);
    }
}
template<typename... Types>
void print(Variant<Types...> const& v)
{
    printImpl<Variant<Types...>, Types...>(v);
}
int main() {
    Variant<int, short, float, double> v(1.5);
    print(v);
}
v.visit([](auto const& value) {
    std::cout << value;
});
class VariantPrinter {
public:
    template<typename T>
    void operator()(T const& value) const
    {
        std::cout << value;
    }
};
// variant/variantvisitimpl.hpp

template<typename R, typename V, typename Visitor,
    typename Head, typename... Tail>
R variantVisitImpl(V&& variant, Visitor&& vis, Typelist<Head, Tail...>) {
    if (variant.template is<Head>()) {
        return static_cast<R>(
            std::forward<Visitor>(vis)(
                std::forward<V>(variant).template get<Head>()));
    }
    else if constexpr (sizeof...(Tail) > 0) {
        return variantVisitImpl<R>(std::forward<V>(variant),
            std::forward<Visitor>(vis),
            Typelist<Tail...>());
    }
    else {
        throw EmptyVariant();
    }
}
// variant/variantvisit.hpp

template<typename... Types>
    template<typename R, typename Visitor>
VisitResult<R, Visitor, Types&...>
Variant<Types...>::visit(Visitor&& vis)& {
    using Result = VisitResult<R, Visitor, Types&...>;
    return variantVisitImpl<Result>(*this, std::forward<Visitor>(vis),
        Typelist<Types...>());
}

template<typename... Types>
    template<typename R, typename Visitor>
VisitResult<R, Visitor, Types const&...>
Variant<Types...>::visit(Visitor&& vis) const& {
    using Result = VisitResult<R, Visitor, Types const &...>;
    return variantVisitImpl<Result>(*this, std::forward<Visitor>(vis),
        Typelist<Types...>());
}

template<typename... Types>
    template<typename R, typename Visitor>
VisitResult<R, Visitor, Types&&...>
Variant<Types...>::visit(Visitor&& vis) && {
    using Result = VisitResult<R, Visitor, Types&&...>;
    return variantVisitImpl<Result>(std::move(*this),
        std::forward<Visitor>(vis),
        Typelist<Types...>());
}

访问结果类型

[](auto const& value) {
    return value + 1;
}
v.visit<Variant<int, double>>([](auto const& value) {
    return value + 1;
});
template<typename R = ComputedResultType, typename Visitor>
VisitResult<R, Visitor, Types&...> visit(Visitor&& vis) &;
class ComputedResultType;
// variant/variantvisitresult.hpp

// an explicitly-provided visitor result type:
template<typename R, typename Visitor, typename... ElementTypes>
class VisitResultT
{
public:
    using Type = R;
};

template<typename R, typename Visitor, typename... ElementTypes>
using VisitResult =
    typename VisitResultT<R, Visitor, ElementTypes...>::Type;
template<typename Visitor, typename... ElementTypes>
class VisitResultT<ComputedResultType, Visitor, ElementTypes...>
{
    ...
}

通用结果类型(Common Result Type)

// variant/commontype.hpp

using std::declval;
template<typename T, typename U>
class CommonTypeT
{
public:
    using Type = decltype(true? declval<T>() : declval<U>());
};

template<typename T, typename U>
using CommonType = typename CommonTypeT<T, U>::Type;
// variant/variantvisitresultcommon.hpp

#include "accumulate.hpp"
#include "commontype.hpp"

// the result type produced when calling a visitor with a value of type T:
template<typename Visitor, typename T>
using VisitElementResult = decltype(declval<Visitor>()(declval<T>()));

// the common result type for a visitor called with each of the given element types:
template<typename Visitor, typename... ElementTypes>
class VisitResultT<ComputedResultType, Visitor, ElementTypes...>
{
    using ResultTypes =
        Typelist<VisitElementResult<Visitor, ElementTypes>...>;
public:
    using Type =
        Accumulate<PopFront<ResultTypes>, CommonTypeT, Front<ResultTypes>>;
};
// variant/variantvisitresultstd.hpp

template<typename Visitor, typename... ElementTypes>
class VisitResultT<ComputedResultType, Visitor, ElementTypes...>
{
public:
    using Type =
        std::common_type_t<VisitElementResult<Visitor, ElementTypes>...>;
};
// variant/visit.cpp

#include "variant.hpp"
#include <iostream>
#include <typeinfo>
int main()
{
    Variant<int, short, double, float> v(1.5);
    auto result = v.visit([](auto const& value) {
        return value + 1;
    });
    std::cout << typeid(result).name() <<'\n';
}

Variant初始化和赋值

默认初始化

// variant/variantdefaultctor.hpp

template<typename... Types>
Variant<Types...>::Variant() {
    *this = Front<Typelist<Types...>>();
}
// variant/variantdefaultctor.cpp

#include "variant.hpp"
#include <iostream>
int main()
{
    Variant<int, double> v;
    if (v.is<int>()) {
        std::cout <<"Default-constructed v stores the int "
            << v.get<int>() <<'\n';
    }
    Variant<double, int> v2;
    if (v2.is<double>()) {
        std::cout <<"Default-constructed v2 stores the double "
            << v2.get<double>() <<'\n';
    }
}
Default-constructed v stores the int 0
Default-constructed v2 stores the double 0

拷贝/移动初始化

// variant/variantcopyctor.hpp

template<typename... Types>
Variant<Types...>::Variant(Variant const& source) {
    if (!source.empty()) {
        source.visit([&](auto const& value) {
            *this = value;
        });
    }
}
// variant/variantmovector.hpp

template<typename... Types>
Variant<Types...>::Variant(Variant&& source) {
    if (!source.empty()) {
        std::move(source).visit([&](auto&& value) {
            *this = std::move(value);
        });
    }
}
// variant/variantcopyctortmpl.hpp

template<typename... Types>
    template<typename... SourceTypes>
Variant<Types...>::Variant(Variant<SourceTypes...> const& source) {
    if (!source.empty()) {
        source.visit([&](auto const& value) {
            *this = value;
        });
    }
}
// variant/variantpromote.cpp

#include "variant.hpp"
#include <iostream>
#include <string>

int main()
{
    Variant<short, float, char const*> v1((short)123);
    Variant<int, std::string, double> v2(v1);
    std::cout << "v2 contains the integer " << v2.get<int>() << '\n';
    v1 = 3.14f;
    Variant<double, int, std::string> v3(std::move(v1));
    std::cout << "v3 contains the double " << v3.get<double>() << '\n';
    v1 ="hello";
    Variant<double, int, std::string> v4(std::move(v1));
    std::cout << "v4 contains the string " << v4.get<std::string>() << '\n';
}
v2 contains the integer 123
v3 contains the double 3.14
v4 contains the string hello

赋值

// variant/variantcopyassign.hpp

template<typename... Types>
Variant<Types...>& Variant<Types...>::operator= (Variant const&
source) {
    if (!source.empty()) {
        source.visit([&](auto const& value) {
            *this = value;
        });
    }
    else {
        destroy();
    }
    return *this;
}
上一篇 下一篇

猜你喜欢

热点阅读