考虑这个简单的模板专业化:
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
:
错误:模板参数'std :: tuple_size&lt; _Tp&gt; :: value' 涉及模板参数
现在让我们用T
模板参数中的typename std::remove_reference<T>::type
替换tuple_size
:
// Using primary structure template from previous example.
template<typename T>
struct S<T, std::tuple_size<typename std::remove_reference<T>::type>::value> {};
此代码仍在模板参数中使用模板参数,但GCC会编译它而不会出现任何错误或警告。为什么呢?
现在,如果我们尝试使用带有/std:c++latest
标志的MSVS编译第二个示例,它会因错误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 Community 2015 Update 3及其默认编译器和GCC 6.2.1。
Tried Clang 3.8.0。它不会编译两个片段,其错误类似于GCC的消息:
错误:非类型模板参数取决于模板参数 部分专业化
答案 0 :(得分:3)
在过去几年中,处理部分类模板专业化可行性的标准的特定部分已经改变了很多次。原始限制[temp.class.spec.match]读取:
部分专用的非类型参数表达式不应涉及部分特化的模板参数,除非参数表达式是简单的标识符。
您的代码明显与此相反,std::tuple_size<T>::value
不是标识符。
然后在cwg issue 1315后更改为:
每个模板参数在非推断的上下文之外的 template-id 中至少出现一次。
但我们可以在那里 - T
在非演绎的上下文中用作第一个模板参数。在template auto
之后,它现在显示为:
如果由于其结构而无法推断出部分特化的模板参数 template-parameter-list 和 template-id ,程序格式不正确。
但我们也可以。可以推断,你有正确的结构&#34; - 您的专业化是在与主要相同的位置使用非类型模板参数,它们应匹配正常。
我认为遵循1315的决议(我认为 post -C ++ 14),代码应该是格式良好的,但gcc和clang都拒绝它。一个不幸的解决方法是使用两个类型的参数:
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和clang都接受了那一个。