模板部分特化用于积分非类型参数和非积分非类型,g ++和clang之间的区别

时间:2016-05-21 23:44:14

标签: c++ templates template-specialization partial-specialization

以下是一个简单的模板部分特化:

// #1
template <typename T, T n1, T n2>
struct foo { 
    static const char* scenario() {
        return "#1 the base template";
    }
};

// #2
// partial specialization where T is unknown and n1 == n2
template <typename T, T a>
struct foo<T, a, a> { 
    static const char* scenario() {
        return "#2 partial specialization";
    }
};

下面的主要内容在g ++(6.1)和clang ++(3.8.0)上得到了不同的结果:

extern const char HELLO[] = "hello";
double d = 2.3;

int main() {
    cout <<   foo<int, 1, 2>                    ::scenario() << endl;                   
    cout <<   foo<int, 2, 2>                    ::scenario() << endl;                   
    cout <<   foo<long, 3, 3>                   ::scenario() << endl;                  
    cout <<   foo<double&, d, d>                ::scenario() << endl;               
    cout <<   foo<double*, &d, &d>              ::scenario() << endl;             
    cout <<   foo<double*, nullptr, nullptr>    ::scenario() << endl;   
    cout <<   foo<int*, nullptr, nullptr>       ::scenario() << endl;      
    cout <<   foo<nullptr_t, nullptr, nullptr>  ::scenario() << endl; 
    cout <<   foo<const char*, HELLO, HELLO>    ::scenario() << endl;
}

结果是g ++和clang

# | The code | g++ (6.1) | clang++ (3.8.0) |
1 | foo<int, 1, 2> | #1 as expected | #1 as expected |
2 | foo<int, 2, 2> | #2 as expected | #2 as expected |
3 | foo<long, 3, 3> | #2 as expected | #2 as expected |
4 | foo<double&, d, d> | #1 -- why? | #2 as expected |
5 | foo<double*, &d, &d> | #2 as expected | #2 as expected |
6 | foo<double*, nullptr, nullptr> | #2 as expected | #1 -- why? |
7 | foo<int*, nullptr, nullptr> | #2 as expected | #1 -- why? |
8 | foo<nullptr_t, nullptr, nullptr> | #2 as expected | #1 -- why? |
9 | foo<const char*, HELLO, HELLO> | #2 as expected | #2 as expected |

哪一个是对的?

代码:http://coliru.stacked-crooked.com/a/45ba16c9f021fd84

2 个答案:

答案 0 :(得分:1)

我认为clang nullptr更像是in-build变量,就像in-build类型int一样。 nullptr实际上没有类型。 nullptr_t的声明(没有定义)是struct nullptr_t nullptr_t;。对于问题的答案是正确的......答案是两者都是正确的,因为有标准,但这些是由不同公司制作的不同编译器,因此GNU G++Clang之间可能存在差异。 Clang应该与G++完全兼容,但事实并非如此。

答案 1 :(得分:-1)

#4形象不对,我很惊讶它编译。首先,double不能用作非类型模板参数。另一方面,类模板使用与用于部分排序的函数模板相同的规则。该标准提供了一个&#34;虚构&#34;为执行排序而生成的函数:

template<int I, int J, class T> class X { };
template<int I, int J> class X<I, J, int> { }; // #1
template<int I>        class X<I, I, int> { }; // #2
template<int I, int J> void f(X<I, J, int>); // A
template<int I>        void f(X<I, I, int>); // B

对于您的示例,它看起来像这样:

template <typename T, T n1, T n2>
struct foo { 
};

template <typename T, T n1, T n2>
void bar(foo<T, n1, n2>)
{
    std::cout << "a";
}

template <typename T, T a>
void bar(foo<T, a, a>)
{
    std::cout << "b";
}

模板参数推导用于确定哪个功能比另一个更专业。 double&应该推导为double,因此两个专业化应该相等,对bar(foo<double&, d, d>{});来说也不明确。看到GCC和Clang抱怨:

  

GCC错误

     

main.cpp:14:6:注意:模板参数扣除/替换   失败:main.cpp:26:29:注意:不匹配的类型&#39; double&amp;&#39;和   &#39;双&#39;

 bar(foo<double&, d, d>{});
                         ^
     

Clang错误

     

注意:候选模板被忽略:替换失败[用T =   double&amp;]:推导出的非类型模板参数不一样   输入相应的模板参数(&#39; double&#39; vs&#39; double   &安培;&#39)

同样,如果删除引用,他们都会正确地抱怨使用double作为非类型模板参数。

我不打算测试其余的,但你可能会发现类似的结果。