mardi 23 août 2016

Is it always necessary to duplicate a class template parameter when enabling constructors based on it?

Generally when using enable_if based on a class' template type, it is necessary to duplicate the class' template type as a template parameter of the constructor or method:

template <
    typename U = T,
    typename = typename std::enable_if<
        !std::is_void<U>::value
    >::type
>
Class() { }

When exactly is this necessary (or not)?

For example, the following code compiles fine on G++, Clang and VC++...

template <typename T = void>
class Class {
public:
    template <
        typename U,
        typename = typename std::enable_if<
            // Is the use of T here allowed?
            std::is_void<T>::value
            || std::is_base_of<T, U>::value
        >::type
    >
    Class(U &&arg) {
        std::cout << "Derived" << std::endl;
    }

    template <
        typename U,
        typename ...U_Rest,
        typename = typename std::enable_if<
            // Is the use of T here allowed?
            !std::is_void<T>::value
            && !std::is_base_of<T, U>::value
        >::type
    >
    Class(U &&arg, U_Rest &&...rest) {
        std::cout << "Not Derived" << std::endl;
    }
};

ideone rextester

... but uses T directly as part of the enable_if. Specifically, if T is void, the "Derived" version of the constructor will always be enabled, and the "Not Derived" version will always be disabled, regardless the parameters U.

Is the above actually legal according to the standard? Or do the compilers just accept it, probably due to a "no diagnostic required"?

Aucun commentaire:

Enregistrer un commentaire