2016-09-15 106 views
5

我一直在试图理解C++选择模板的方式。也就是说,考虑下面的代码示例:会员功能模板选择和SFINAE

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 

前两个功能(测试1)正常工作(为什么):

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

虽然他们的休息导致编译错误。 关于test2的编译器要求我试图创建一个重复的功能:

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

Here的文件说:

一个常见的错误是声明中的区别仅 二元函数模板,其默认模板参数。这是非法的,因为默认 模板参数不是函数模板签名的一部分,并且 声明具有相同签名的两个不同函数模板是 非法。

所以它似乎是这种情况。但是,我没有看到与前两个函数有很大区别,它们也有默认的模板参数。因此,我们有一个默认类型(test2 - 不起作用)与默认值(test1 - works)。有关于它的规则吗?

在test3的情况下:

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

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

据我所知,在第五种情况下,添加额外的参数使得test5签名依赖于函数模板参数,因此SFINAE踢入和解析工作。

我很困惑C++如何处理这些模板。有人可以善待这些事吗?

+1

作为一个说明,因为C++ 14,你可以替换'类型名称的std :: enable_if :: type'用'的std :: enable_if_t '。 – Jarod42

回答

6
  • 在默认值移除,为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(); 
    

    这些显然是相同的签名。

  • 对于TEST3,SFINAE并不像你一样R固定在班级和你enable_if不依赖于功能的模板参数具有硬错误适用。

  • 对于TEST4,有关于模板函数签名的例外,因为超载会返回类型只相差这么

    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,所以也可以。

+0

谢谢,我刚刚在原始文章中添加了两个例子 – mentalmushroom

+0

添加了test4/test5的解释:'enable_if'在两种情况下都取决于'T'。 – Jarod42