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

时间:2022-06-30 11:21:07

I'd like to ask whether the following code sample should compile:

我想问一下以下代码示例是否应该编译:

#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();
}

The code compiles fine with GCC 4.8.1 and Clang 3.4 but not with Visual Studio 2013. The error I get:

代码可以使用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

What does the standard say about this? Am I required to explicitly state all the parameters (including defaulted ones) when using the template template parameter C or is this just a bug in VC++?

标准对此有何看法?我是否需要在使用模板模板参数C时明确声明所有参数(包括默认参数),或者这只是VC ++中的一个错误?

Context: The issue araised from Constructor's answer to my previous question: https://*.com/a/23874768/2617356

上下文:从构造函数回答上一个问题的问题:https://*.com/a/23874768/2617356

When searching the archives I've found this question: Default values in templates with template arguments ( C++ ) It's basically about the same problem, the question author states that default parameters for template template parameter "had to be" explicitly stated. However, the asker accepted solution that's not quite applicable in my case. The question was not about what is the standard-conforming behaviour, so I believe this is not a duplicate.

在搜索档案时,我发现了这个问题:带有模板参数的模板中的默认值(C ++)这基本上是关于同一个问题,问题作者声明模板模板参数的默认参数“必须”明确说明。但是,提问者接受的解决方案在我的案例中并不适用。问题不在于什么是符合标准的行为,所以我认为这不是重复的。

2 个解决方案

#1


2  

Consider the similar

考虑类似的

template <typename = void, int = 0> struct A { };
template <template <typename ...> class T> struct B : T<> { };
template class B<A>;

This is clearly covered by the standard (14.3.3p3 if you're interested, I won't quote it, as GCC and clang do both implement the rule already), where the use of A as a template argument for B is disallowed because of the non-type template parameter. That rule makes no sense if the instantiation of a template template parameter could make use of the template template argument's default template arguments, so the behaviour of MSVC and Intel is more consistent than that of GCC and clang.

标准明确涵盖了这一点(14.3.3p3,如果你感兴趣,我不会引用它,因为GCC和clang都已经实现了规则),其中不允许使用A作为B的模板参数,因为非类型模板参数。如果模板模板参数的实例化可以使用模板模板参数的默认模板参数,则该规则没有意义,因此MSVC和Intel的行为比GCC和clang的行为更一致。

Of course, the reasoning "if this were valid, the standard would have inconsistencies" doesn't actually mean it isn't valid, only that it shouldn't be valid. To actually check what the standard says:

当然,推理“如果这是有效的,标准会有不一致”并不意味着它无效,只是它不应该是有效的。要实际检查标准的含义:

14.1 Template parameters [temp.param]

14.1模板参数[temp.param]

10 The set of default template-arguments available for use with a template declaration or definition is obtained by merging the default arguments from the definition (if in scope) and all declarations in scope in the same way default function arguments are (8.3.6).

10可用于模板声明或定义的默认模板参数集是通过合并定义中的默认参数(如果在范围内)和范围内的所有声明(默认函数参数为(8.3.6))获得的。

8.3.6 Default arguments [dcl.fct.default]

8.3.6默认参数[dcl.fct.default]

4 Declarations in different scopes have completely distinct sets of default arguments. That is, declarations in inner scopes do not acquire default arguments from declarations in outer scopes, and vice versa.

4不同范围内的声明具有完全不同的默认参数集。也就是说,内部作用域中的声明不从外部作用域中的声明获取默认参数,反之亦然。

Although not specifically intended to address this use of default template arguments, I think it does manage to do so. Nikos Athanasiou has already included the part of the standard that says any default template arguments of C do get used:

虽然不是专门用于解决这种默认模板参数的使用,但我认为它确实设法这样做。 Nikos Athanasiou已经包含了标准的一部分,该标准表示C的任何默认模板参数都会被使用:

14.1 Template parameters [temp.param]

14.1模板参数[temp.param]

14 A template-parameter of a template template-parameter is permitted to have a default template-argument. When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter.

14模板模板参数的模板参数允许具有默认模板参数。如果指定了此类默认参数,则它们将应用于模板template-parameter范围内的模板template-parameter。

Since C's default template arguments are used, std::vector's aren't, and MSVC and Intel seem to be correct here.

由于使用了C的默认模板参数,因此std :: vector不是,并且MSVC和Intel似乎在这里是正确的。

And to come up with an example that clearly shows that GCC and clang cannot be considered to conform here:

并想出一个例子,清楚地表明GCC和clang不能被认为符合这里:

template <typename = char, typename = short>
struct A { };

template <template <typename = void, typename ...> class T>
struct B {
  using type = T<>;
};

Both GCC and clang treat B<A>::type as A<void, short>, taking one default template argument from T, and another from A, even though the standard disallows merging of default arguments (and hence default template arguments) in declarations in different scopes.

GCC和clang都将B :: type视为A ,从T获取一个默认模板参数,从A获取另一个,即使标准不允许合并默认参数(以及默认模板参数)不同范围内的声明。 ,short>


A workaround for you, to avoid the need to type out the allocator argument, could be to use a template alias:

为避免需要输入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();
}

I cannot test on MSVC right now, but Intel accepts it, and I see no reason why this variant would be invalid.

我现在无法在MSVC上进行测试,但英特尔接受了它,我认为没有理由为什么这个变体无效。

#2


1  

A (seemingly related) quote from the standard 14.1 Template parameters

标准14.1模板参数的(看似相关的)引用

14 . A template-parameter of a template template-parameter is permitted to have a default template-argument. When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter.

14。允许模板模板参数的模板参数具有默认模板参数。如果指定了此类默认参数,则它们将应用于模板template-parameter范围内的模板template-parameter。

[ Example:

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>
}

— end example ]

- 结束例子]

This is the only verse posing limitations to the use of default template parameters of template template parameters (verse 9,11,12 pose limitations on the definition/specification)

这是对模板模板参数的默认模板参数的使用构成限制的唯一一节(第9,11,12节对定义/规范构成限制)

As stressed in the comments, OP's case does not involve a default parameter in convert_container (so the above does not apply explicitly). IMHO there are two ways of interpreting the situation :

正如评论中强调的那样,OP的案例不涉及convert_container中的默认参数(因此上述内容不明确适用)。恕我直言有两种方式来解释这种情况:

  1. using type = C<double> is a type alias for a class template; that class "loses" the right to use default template parameters, since it's passed as a template template parameter and all default arguments (of that TT parameter) lie outside the scope of the "typedefing". Then VS is correct.

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

  2. By tracking the instantiation process : Say a correct compiler instantiates the struct as so (it's just a type substitution - no actual representation of the actual instantiation process is implied)

    通过跟踪实例化过程:假设一个正确的编译器实例化结构(它只是一个类型替换 - 没有暗示实际实例化过程的实际表示)

    struct convert_container
    {
        using type = vector<double>; 
    };
    

then OP's case seems fairly legit (and gcc/clang are correct)

那么OP的案例似乎相当合法(并且gcc / clang是正确的)


FWIW

This compiles in VS2013

这在VS2013中编译

template <template <class...> class C>
using tt = C<double>;

int main()
{
    std::cout << typeid(tt<std::vector>).name();
}

So the arguments of default template parameters being non legal to pass to template template parameters seems more and more shaky.

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

#1


2  

Consider the similar

考虑类似的

template <typename = void, int = 0> struct A { };
template <template <typename ...> class T> struct B : T<> { };
template class B<A>;

This is clearly covered by the standard (14.3.3p3 if you're interested, I won't quote it, as GCC and clang do both implement the rule already), where the use of A as a template argument for B is disallowed because of the non-type template parameter. That rule makes no sense if the instantiation of a template template parameter could make use of the template template argument's default template arguments, so the behaviour of MSVC and Intel is more consistent than that of GCC and clang.

标准明确涵盖了这一点(14.3.3p3,如果你感兴趣,我不会引用它,因为GCC和clang都已经实现了规则),其中不允许使用A作为B的模板参数,因为非类型模板参数。如果模板模板参数的实例化可以使用模板模板参数的默认模板参数,则该规则没有意义,因此MSVC和Intel的行为比GCC和clang的行为更一致。

Of course, the reasoning "if this were valid, the standard would have inconsistencies" doesn't actually mean it isn't valid, only that it shouldn't be valid. To actually check what the standard says:

当然,推理“如果这是有效的,标准会有不一致”并不意味着它无效,只是它不应该是有效的。要实际检查标准的含义:

14.1 Template parameters [temp.param]

14.1模板参数[temp.param]

10 The set of default template-arguments available for use with a template declaration or definition is obtained by merging the default arguments from the definition (if in scope) and all declarations in scope in the same way default function arguments are (8.3.6).

10可用于模板声明或定义的默认模板参数集是通过合并定义中的默认参数(如果在范围内)和范围内的所有声明(默认函数参数为(8.3.6))获得的。

8.3.6 Default arguments [dcl.fct.default]

8.3.6默认参数[dcl.fct.default]

4 Declarations in different scopes have completely distinct sets of default arguments. That is, declarations in inner scopes do not acquire default arguments from declarations in outer scopes, and vice versa.

4不同范围内的声明具有完全不同的默认参数集。也就是说,内部作用域中的声明不从外部作用域中的声明获取默认参数,反之亦然。

Although not specifically intended to address this use of default template arguments, I think it does manage to do so. Nikos Athanasiou has already included the part of the standard that says any default template arguments of C do get used:

虽然不是专门用于解决这种默认模板参数的使用,但我认为它确实设法这样做。 Nikos Athanasiou已经包含了标准的一部分,该标准表示C的任何默认模板参数都会被使用:

14.1 Template parameters [temp.param]

14.1模板参数[temp.param]

14 A template-parameter of a template template-parameter is permitted to have a default template-argument. When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter.

14模板模板参数的模板参数允许具有默认模板参数。如果指定了此类默认参数,则它们将应用于模板template-parameter范围内的模板template-parameter。

Since C's default template arguments are used, std::vector's aren't, and MSVC and Intel seem to be correct here.

由于使用了C的默认模板参数,因此std :: vector不是,并且MSVC和Intel似乎在这里是正确的。

And to come up with an example that clearly shows that GCC and clang cannot be considered to conform here:

并想出一个例子,清楚地表明GCC和clang不能被认为符合这里:

template <typename = char, typename = short>
struct A { };

template <template <typename = void, typename ...> class T>
struct B {
  using type = T<>;
};

Both GCC and clang treat B<A>::type as A<void, short>, taking one default template argument from T, and another from A, even though the standard disallows merging of default arguments (and hence default template arguments) in declarations in different scopes.

GCC和clang都将B :: type视为A ,从T获取一个默认模板参数,从A获取另一个,即使标准不允许合并默认参数(以及默认模板参数)不同范围内的声明。 ,short>


A workaround for you, to avoid the need to type out the allocator argument, could be to use a template alias:

为避免需要输入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();
}

I cannot test on MSVC right now, but Intel accepts it, and I see no reason why this variant would be invalid.

我现在无法在MSVC上进行测试,但英特尔接受了它,我认为没有理由为什么这个变体无效。

#2


1  

A (seemingly related) quote from the standard 14.1 Template parameters

标准14.1模板参数的(看似相关的)引用

14 . A template-parameter of a template template-parameter is permitted to have a default template-argument. When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter.

14。允许模板模板参数的模板参数具有默认模板参数。如果指定了此类默认参数,则它们将应用于模板template-parameter范围内的模板template-parameter。

[ Example:

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>
}

— end example ]

- 结束例子]

This is the only verse posing limitations to the use of default template parameters of template template parameters (verse 9,11,12 pose limitations on the definition/specification)

这是对模板模板参数的默认模板参数的使用构成限制的唯一一节(第9,11,12节对定义/规范构成限制)

As stressed in the comments, OP's case does not involve a default parameter in convert_container (so the above does not apply explicitly). IMHO there are two ways of interpreting the situation :

正如评论中强调的那样,OP的案例不涉及convert_container中的默认参数(因此上述内容不明确适用)。恕我直言有两种方式来解释这种情况:

  1. using type = C<double> is a type alias for a class template; that class "loses" the right to use default template parameters, since it's passed as a template template parameter and all default arguments (of that TT parameter) lie outside the scope of the "typedefing". Then VS is correct.

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

  2. By tracking the instantiation process : Say a correct compiler instantiates the struct as so (it's just a type substitution - no actual representation of the actual instantiation process is implied)

    通过跟踪实例化过程:假设一个正确的编译器实例化结构(它只是一个类型替换 - 没有暗示实际实例化过程的实际表示)

    struct convert_container
    {
        using type = vector<double>; 
    };
    

then OP's case seems fairly legit (and gcc/clang are correct)

那么OP的案例似乎相当合法(并且gcc / clang是正确的)


FWIW

This compiles in VS2013

这在VS2013中编译

template <template <class...> class C>
using tt = C<double>;

int main()
{
    std::cout << typeid(tt<std::vector>).name();
}

So the arguments of default template parameters being non legal to pass to template template parameters seems more and more shaky.

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