使用模板模板参数时是否需要显式列出默认参数?

时间:2014-06-03 14:12:52

标签: c++ templates

我想问下面的代码示例是否应该编译:

#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++ )这基本上是关于同样的问题,问题作者声明模板模板参数的默认参数“必须”明确说明。但是,提问者接受的解决方案在我的案例中并不适用。问题不在于什么是符合标准的行为,所以我认为这不是重复的。

2 个答案:

答案 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中的默认参数(因此上述内容未明确适用)。恕我直言,有两种方法可以解释这种情况:

  1. using type = C<double>是类模板的类型别名;该类“失去”使用默认模板参数的权利,因为它作为模板模板参数传递,并且所有默认参数(该TT参数)都在“typedefing”的范围之外。然后VS是正确的。

  2. 通过跟踪实例化过程:假设一个正确的编译器将结构实例化为(它只是一个类型替换 - 不暗示实际实例化过程的实际表示)< / SUP>

    struct convert_container
    {
        using type = vector<double>; 
    };
    
  3. 然后OP的案例似乎相当合法(并且gcc / clang是正确的)


    FWIW

    在VS2013中编译

    template <template <class...> class C>
    using tt = C<double>;
    
    int main()
    {
        std::cout << typeid(tt<std::vector>).name();
    }
    

    因此,传递给模板模板参数的默认模板参数的参数似乎越来越不合理。

相关问题