在启用基于它的构造函数时是否总是需要复制 class 模板参数?

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

一般在使用基于class'模板类型的enable_if时,需要复制class'模板类型作为构造函数或方法的模板参数:

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

到底什么时候需要(或不需要)?

例如,以下代码在 G++、Clang 和 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

...但直接使用 T 作为 enable_if 的一部分。具体来说,如果 Tvoid,则 "Derived" 版本的构造函数将始终启用,而 "Not Derived" 版本将始终禁用,无论参数 U.

根据标准,以上实际上是合法的吗?或者编译器只是接受它,可能是由于 "no diagnostic required"?

SFINAE适用于模板方法,应该不是hard failure(所以主要看它的模板参数)

这里你的条件取决于U,所以没关系。

注意你应该更喜欢

template <typename U, std::enable_if_t<cond>* = nullptr>

超过

template <typename U, typename = std::enable_if_t<cond>>

允许写入禁用版本

template <typename U, std::enable_if_t<!cond>* = nullptr>

template <typename U, typename = std::enable_if_t<cond>> //...
template <typename U, typename = std::enable_if_t<!cond>> // ...

有相同的签名。