我想问下面的代码示例是否应该编译:
#include <iostream>
#include <vector>
#include <typeinfo>
using namespace std;
template <template <class...> class C>
struct convert_container
{
using type = C<double>;
// Visual Studio requires this to be:
// using type = C<double, std::allocator<doble>>
};
int main()
{
std::cout << typeid(convert_container<std::vector>::type).name();
}
代码可以使用GCC 4.8.1和Clang 3.4进行编译,但不适用于Visual Studio 2013.我得到错误:
error C2976: 'std::vector' : too few template arguments
c:\program files (x86)\microsoft visual studio 12.0\vc\include\vector(650) : see declaration of 'std::vector'
c:\users\michał\documents\visual studio 2013\projects\transform\transform.cpp(14) : see reference to class template instantiation 'convert_container<std::vector>' being compiled
标准对此有何评价?我是否需要在使用模板模板参数C
时明确声明所有参数(包括默认参数),或者这只是VC ++中的错误?
上下文:从构造函数对上一个问题的回答中提出的问题:https://stackoverflow.com/a/23874768/2617356
在搜索档案时,我发现了这个问题:Default values in templates with template arguments ( C++ )这基本上是关于同样的问题,问题作者声明模板模板参数的默认参数“必须”明确说明。但是,提问者接受的解决方案在我的案例中并不适用。问题不在于什么是符合标准的行为,所以我认为这不是重复的。
答案 0 :(得分:2)
考虑类似的
template <typename = void, int = 0> struct A { };
template <template <typename ...> class T> struct B : T<> { };
template class B<A>;
标准明确涵盖了这一点(14.3.3p3如果您感兴趣,我不会引用它,因为GCC和clang都已经实施了规则),其中使用A
作为由于非类型模板参数,不允许B
的模板参数。如果模板模板参数的实例化可以使用模板模板参数的默认模板参数,则该规则没有意义,因此MSVC和Intel的行为比GCC和clang的行为更加一致。
当然,推理“如果这是有效的,标准会有不一致”并不意味着它无效,只是它不应该是有效的。要实际检查标准是什么:
14.1模板参数[temp.param]
10可用于模板声明或定义的默认 template-arguments 集合是通过合并定义中的默认参数(如果在范围内)和范围内的所有声明来获得的。方式默认函数参数是(8.3.6)。
8.3.6默认参数[dcl.fct.default]
4不同范围内的声明具有完全不同的默认参数集。也就是说,内部作用域中的声明不从外部作用域中的声明中获取默认参数,反之亦然。
虽然并非专门用于解决默认模板参数的使用问题,但我认为它确实设法这样做。 Nikos Athanasiou已经包含了标准的一部分,该标准表示C
的任何默认模板参数都被使用:
14.1模板参数[temp.param]
14模板 template-parameter 的 template-parameter 允许使用默认的 template-argument 。指定此类默认参数后,它们将应用于模板 template-parameter 范围内的模板 template-parameter 。
由于使用了C
的默认模板参数,std::vector
不是,而且MSVC和英特尔似乎在这里是正确的。
并想出一个例子,清楚地表明GCC和clang不能被认为符合这里:
template <typename = char, typename = short>
struct A { };
template <template <typename = void, typename ...> class T>
struct B {
using type = T<>;
};
GCC和clang都将B<A>::type
视为A<void, short>
,从T
获取一个默认模板参数,从A
获取另一个默认模板参数,即使标准不允许合并默认参数(以及默认模板参数)在不同范围的声明中。
为避免需要输入allocator参数,您可以使用模板别名来解决此问题:
template <template <class...> class C>
struct convert_container
{
using type = C<double>;
};
template <typename T>
using vector_default_alloc = std::vector<T>;
int main()
{
std::cout << typeid(convert_container<vector_default_alloc>::type).name();
}
我现在无法在MSVC上进行测试,但英特尔接受了它,我认为没有理由认为此变体无效。
答案 1 :(得分:1)
标准14.1 Template parameters
14 p。允许模板模板参数的模板参数具有默认模板参数。如果指定了此类默认参数,它们将应用于模板模板参数范围内的模板模板参数。
[例如:
template <class T = float> struct B {};
template <template <class TT = float> class T> struct A {
inline void f();
inline void g();
};
template <template <class TT> class T> void A<T>::f() { // (*)
T<> t; // error - TT has no default template argument
}
template <template <class TT = char> class T> void A<T>::g() {
T<> t; // OK - T<char>
}
- 结束示例]
这是对模板模板参数的默认模板参数的使用构成限制的唯一一节(第9,11,12节对定义/规范的限制)
正如评论中所强调的那样,OP的案例不涉及convert_container
中的默认参数(因此上述内容未明确适用)。恕我直言,有两种方法可以解释这种情况:
using type = C<double>
是类模板的类型别名;该类“失去”使用默认模板参数的权利,因为它作为模板模板参数传递,并且所有默认参数(该TT参数)都在“typedefing”的范围之外。然后VS是正确的。
通过跟踪实例化过程:假设一个正确的编译器将结构实例化为(它只是一个类型替换 - 不暗示实际实例化过程的实际表示)< / SUP>
struct convert_container
{
using type = vector<double>;
};
然后OP的案例似乎相当合法(并且gcc / clang是正确的)
在VS2013中编译
template <template <class...> class C>
using tt = C<double>;
int main()
{
std::cout << typeid(tt<std::vector>).name();
}
因此,传递给模板模板参数的默认模板参数的参数似乎越来越不合理。