成员函数模板选择和SFINAE

Member function template selection and SFINAE

我一直在努力理解 C++ 选择模板的方式。即,考虑以下代码示例:

template <typename R>
class Curious
{
public:
    template <typename T, typename std::enable_if<std::is_const<T>::value, int>::type = 33>
    void test1() {}

    template <typename T, typename std::enable_if<!std::is_const<T>::value, int>::type = 33>
    void test1() {}

    template <typename T, typename = typename std::enable_if<std::is_const<T>::value>::type>
    void test2() {}

    template <typename T, typename = typename std::enable_if<!std::is_const<T>::value>::type>
    void test2() {}

    template <typename std::enable_if<std::is_const<R>::value>::type * = nullptr>
    void test3() {}

    template <typename std::enable_if<!std::is_const<R>::value>::type * = nullptr>
    void test3() {}

    // works
    template <typename T = void>
    typename std::enable_if<std::is_const<R>::value, T>::type test4() {}

    template <typename T = void>
    typename std::enable_if<!std::is_const<R>::value, T>::type test4() {}

    // also works
    template <typename T = void, typename std::enable_if<std::is_const<R>::value, T>::type * = nullptr>
    void test5() {}

    template <typename T = void, typename std::enable_if<!std::is_const<R>::value, T>::type * = nullptr>
    void test5() {}
}; // Curious

前两个函数 (test1) 工作正常(为什么?):

Curious<int> curious;
curious.test1<int>();
curious.test1<const int>();

而其余的则导致编译错误。 关于函数 test2,编译器声称我正在尝试创建一个副本:

error C2535: 'void Curious::test2(void)': member function already defined or declared

Here 文档说:

A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.

看来是这样的。但是,我看不出与前两个函数有什么区别,前两个函数也有默认的模板参数。因此我们有一个默认类型(test2 - 不工作)反对默认值(test1 - 工作)。有什么规定吗?

如果是 test3:

error C2039: 'type': is not a member of 'std::enable_if'
与第一种情况一样,这次成员函数模板有一个默认的非类型参数,但它依赖于 class 模板参数。现在 SFINAE 不会跳过错误的(也不知道为什么)。

第四种情况SFINAE通过return类型解析模板。但是这些 test4 函数不具有相同的签名吗?因为它们仅在 return 类型上有所不同。

据我了解,在第五种情况下,添加额外参数会使 test5 签名依赖于函数模板参数,因此 SFINAE 启动并解决问题。

我对 C++ 如何处理这些模板感到很困惑。有人能帮我清理一下这些东西吗?

  • 删除默认值后,对于 test1,您有:

    template <typename T, typename std::enable_if<std::is_const<T>::value, int>::type>
    void test1();
    
    template <typename T, typename std::enable_if<!std::is_const<T>::value, int>::type>
    void test1();
    

    签名明显不同。

  • 对于测试 2:

    template <typename T, typename> void test2();
    
    template <typename T, typename> void test2();
    

    明显是相同的签名。

  • 对于测试 3,SFINAE 不适用,因为您有硬错误,因为 R 在 class 中已修复并且您的 enable_if 不依赖于函数的模板参数。

  • 对于 test4,模板函数的签名有一个例外,因为重载可能仅 return 类型不同所以

    int foo();
    char foo(); // Illegal.
    

    但是

    template <typename T> int foo();
    template <typename T> char foo(); // legal, even it is not trivial to call
    

    另外,std::enable_if<!std::is_const<R>::value, T>::type依赖于模板参数T所以没问题。

  • 对于test5,第二个模板参数依赖于第一个模板参数T,所以也可以。