Search code examples
c++c++17copy-constructorperfect-forwardingstdarray

How to disable overly generic forwarding constructor in c++17 and defer to copy constructor


If I have the simple class written using c++20 concepts


#include <array>
#include <type_traits>

template <class T, int N>
struct MyVec {
    std::array<T, N> m_vec;

    template <typename... Q>
    MyVec(Q&&... args) 
    requires (std::is_same_v<Q,T> && ...): 
    m_vec{std::forward<Q>(args)...} {}
};

and an example of it being used

int main(int argc, char* argv[]) {
    using V = MyVec<double, 2>;
    V v0(1., 2.);
    V v1(1., 2.);

    V v2 = v0;
}

The requires statement prevents the copy constructor from being hidden by the overly generic forwarding constructor. See https://godbolt.org/z/8Me19v7Ks

If you delete the requires statement then the line V v2 = v0 will fail to compile.

However I can't use concepts because this needs to be in a c++17 only context. I'm stuck trying to figure out how to use enable_if to protect the constructor. I could mark the construct as explicit but I'd prefer not to.


Solution

  • You can use enable_if to disable the template:

    template <class T, int N>
    struct MyVec {
        std::array<T, N> m_vec;
    
        template <typename... Q,
            std::enable_if_t<(std::is_convertible_v<Q, T> && ...), int> = 0>
        MyVec(Q&&... args) 
        : m_vec{std::forward<Q>(args)...} {}
    };
    

    I am not an expert with concepts, but most (all?) of what you can do with requires or similar, you can achieve with enable_if, although it's usually a bit uglier (and error prone) and will lead to much less readable errors from compilers.