2017-02-09 36 views
2

考虑这个简单的模板专业化:模板偏特非类型参数:GCC VS MSVS

template<typename T, size_t I> 
struct S {}; 

template<typename T> 
struct S<T, std::tuple_size<T>::value> {}; 

GCC不能编译它,因为它使用模板参数T在模板参数std::tuple_size<T>::value

error: template argument 'std::tuple_size<_Tp>::value' involves template parameter(s)

现在,让我们在tuple_size模板参数与typename std::remove_reference<T>::type替换T

// Using primary structure template from previous example. 
template<typename T> 
struct S<T, std::tuple_size<typename std::remove_reference<T>::type>::value> {}; 

该代码仍然在模板参数中使用模板参数,但GCC编译时没有任何错误或警告。为什么?

non-type parameter of a partial specialization must be a simple identifier

这是什么奇怪的限制:

现在,如果我们试图编译使用MSVS的第二个例子是/std:c++latest标志,它与错误C2755停止?当I等于元组大小时,我想停止编译时递归。

那么他们是谁错了:MSVS还是GCC?

注意MSVS即使没有任何模板实例报告错误,而GCC正常工作与所有这些实例:

S<std::tuple<int, float>, 9> s1; 
S<std::tuple<int, float>, 2> s2; 
S<int, 42> s3; 

我使用MSVS社区2015年更新3与它的默认编译器和GCC 6.2.1。

尝试铿锵3.8.0。它不具有类似于GCC的消息的错误编译两个片段:

error: non-type template argument depends on a template parameter of the partial specialization

回答

3

处理部分类模板特化的可行性标准的特定部分已在过去几年改变了很多次。原restrictionin [temp.class.spec.match]读:

A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier.

您的代码明确运行相抵触的是,std::tuple_size<T>::value不是标识符。

它再变,cwg issue 1315后,将其改为:

Each template-parameter shall appear at least once in the template-id outside a non-deduced context.

但我们现在还好吧 - T是在非推断上下文的第一个模板参数使用。和template auto之后,它现在读取:

If the template arguments of a partial specialization cannot be deduced because of the structure of its template-parameter-list and the template-id, the program is ill-formed.

但我们还好有太多。可以推论,你有正确的“结构” - 你的专业化在主要位置使用一个非类型的模板参数,它们应该很好地匹配。


我认为以下的1315的分辨率(我认为这是 -C++ 14),所述代码应良好的,但两者gcc和铛拒绝它。不幸的解决将是使用两个参数代替:

template <class T, class I> 
struct S; 

template<typename T> 
struct S<T, typename std::tuple_size<T>::type> {}; 

template <size_t I> 
using size_ = std::integral_constant<size_t, I>; 

int main() { 
    S<std::tuple<int>, size_<1>> s; 
} 

GCC和铿锵接受一个。