Search code examples
c++templatesc++17template-meta-programmingvariant

How to deduce the type of template argument based on some conditions and return information about that type


Suppose I have a class C that has holds a std::variant<A, B>. The constructor of C must be a templated constructor that can accept any type, and based on that type it must initialize the variant in different ways.

Here is a simplified overview:

struct A {
    // ...
};

struct B {
    // ...
};

class C {
    public:
        template <typename T>
        C(T arg) {
            if constexpr (std::same_v<B, T>) {
                var = B{arg};
            }
            else if constexpr (std::is_constructible<A, T>) {
                var = A{arg};
            }
        }

    private:
        std::variant<A, B> var;
};

I am looking for a way to squash these if constexpr statements by using some template metaprogramming magic, so I may rewrite the constructor like this (hoping to avoid the extra initialization of the variant):

template<T>
struct DeduceAOrB {
  // ..... somehow check the conditions here 
};

template <typename T>
        C(T arg)
            : var(DeduceAOrB<T>::type{arg})
        {}

Important note is that the variant may need to be expanded in the future, so the solution must be viable for arbitrarily many types


Solution

  • One way is to create a helper function containing the conditions and use it in initializer list as shown below:

    class C {
        private:
            //helper function checking for different conditions and returning based on result
            template<typename T>
            static constexpr auto helperFunc(T arg)
            {
                if constexpr (std::is_same_v<B, T>) {
                            return B{arg};
                }
                else if constexpr (std::is_constructible_v<A, T>) {
                            return A{arg};
                } else {
                            return A{};
                }
            }
        public:
            template <typename T>
    //----------------VVV------------------>use helperFunc initializer list
            C(T arg): var(helperFunc(arg)){
                
                
            }
    
        private:
            std::variant<A, B> var;
    };