Chained conversion between classes without public inheritances

后端 未结 2 1953
挽巷
挽巷 2021-01-16 12:54

Question

I have a series of ~10 template classes A, B, C, D, ...

I want to enable conversions from a class to previous classes in the series

相关标签:
2条回答
  • 2021-01-16 13:24

    You can achieve this by constraining a templated constructor (which will be used in conversion) using std::enable_if and some template metaprogramming:

    template <template <class> typename BaseTemplate,
              typename From,
              typename To,
              typename Enable = void>
    struct may_convert
        : public std::false_type {};
    
    template <template <class> typename BaseTemplate,
              typename T>
    struct may_convert<BaseTemplate, BaseTemplate<T>, BaseTemplate<T>, void>
        : public std::true_type {};
    
    template <template <class> typename BaseTemplate,
              typename T,
              typename U>
    struct may_convert<BaseTemplate, BaseTemplate<T>, BaseTemplate<U>, 
                       typename std::enable_if<!std::is_same<T, U>::value>::type>
        : public may_convert<BaseTemplate, T, BaseTemplate<U>> {};
    

    may_convert will walk up the templates of the From template parameter until it is equal to To (in which case it inherits from std::true_type, i.e. may_convert<...>::value is true), or until the templates run out (in which case may_convert<...>::value is false).

    Now, all that remains is constraining your constructor appropriately:

    template <typename Convertible>
    class A {
    public:
        A() {}
    
        template <typename T,
                  typename = typename std::enable_if<
                      may_convert<A, T, A<Convertible>>::value>::type>
        A(const T&) {}
    };
    

    This way, the constructor exists only if may_convert<...>::value is true. Otherwise, the conversion will fail.


    Examples

    Here is an example of how may_convert works in your example (converting from D = A<A<A<int>>> to B = A<int>):

    • The constructor only exists if may_convert<A, D, B>::value is true

    • may_convert<A, D, B> matches the last specialization (because D = A<C> and B = A<int>, the parameters are deduced as T = C and U = int) and inherits from may_convert<A, C, B>

    • may_convert<A, C, B> again matches the last specialization (T = B, U = int) and inherits from may_convert<A, B, B>

    • This time, the two types are equal, so the first specialization matches, and the entire thing inherits from std::true_type, enabling the constructor.

    On the other hand, imagine a using E = A<double> that should not convert to B:

    • The constructor will only be enabled if may_convert<A, E, B>::value is true

    • may_convert<A, E, B> matches the last specialization, and inherits from may_convert<A, double, B>

    • Because double is not an A<...>, none of the specializations match, so we fall back to the default case, which inherits from std::false_type.

    • Therefore, may_convert<A, E, B>::value is false, and the conversion fails.

    0 讨论(0)
  • 2021-01-16 13:35

    Although @hlt's approach will do what you ask, without knowing more about the context, I'm wary about implementing the conversion in A. In the cases I can think of, A shouldn't be aware of B, C or D, so I'll suggest a different implementation.

    You can create a variant of your test 3, where you implement one conversion operator per class, but where you also inherit a templated indirect conversion operator, like so:

    #include <type_traits>
    
    template <typename T1, typename T2>
    struct indirect_conversion {
        template <typename T, typename = std::enable_if_t<std::is_constructible_v<T, T2>>>
        operator T() {
            return static_cast<T1 *>(this)->operator T2();
        }
    };
    
    struct A {};
    
    struct B : indirect_conversion<B, A> {
        operator A();
    };
    struct C : indirect_conversion<C, B> {
        operator B();
    };
    struct D : indirect_conversion<D, C> {
        operator C();
    };
    A a = D();
    
    0 讨论(0)
提交回复
热议问题