【问题标题】:Template template parameters and default arguments模板模板参数和默认参数
【发布时间】:2013-10-15 20:16:33
【问题描述】:

考虑以下代码,它使用“模板模板”参数来实例化一个使用多种类型的类模板:

#include <iostream>
using namespace std;

enum E
{
    a = 0,
    b = 1
};

template <template <E> class Action, class T>
void do_something(const T& value)
{
    typedef Action<a> type1;
    typedef Action<b> type2;
}

template <E e, class Enable = void>
class Foo
{

};

int main()
{
    do_something<Foo>(int(55));
}

使用较旧的编译器(GCC 4.1.2),上面的代码编译得很好。但是,使用较新的编译器(GCC 4.4.6 或 4.8.1)会产生以下错误:

test3.cpp:25:27: error: no matching function for call to ‘do_something(int)’
  do_something<Foo>(int(55));

所以看起来 GCC 无法绑定到 do_something,因为模板模板参数只声明了一个参数(一个 Enum),但 Foo 实际上接受了两个模板参数(即使一个是默认的)。我猜测 GCC 4.1.2 允许忽略默认参数。

好的,如果我将模板定义更改为:

template <template <E, class> class Action, class T>
void do_something(const T& value)
{
    typedef Action<a> type1;
    typedef Action<b> type2;
}

...那么我测试的任何版本的 GCC 都不会编译它。它们都产生类似的错误:

test3.cpp:13: error: wrong number of template arguments (1, should be 2)
test3.cpp:10: error: provided for ‘template<E <anonymous>, class> class Action’

所以现在,编译器抱怨,因为表达式typedef Action&lt;a&gt; type1 只提供了一个模板参数。显然,我无法在此处隐式使用默认参数。

有什么方法可以在模板模板函数中使用模板的默认参数吗?

【问题讨论】:

  • 在您对do_someting 的最终定义中,您能否将第一个class 替换为class=void?这可能会告诉它Action 是一个带有一个默认值的双参数模板。我想我希望它起作用,因为这意味着默认值被指定了两次!
  • 这确实有效。这是标准允许的吗?
  • 这是我的一个疯狂猜测!不知道标准。我在 g++-4.6.3 上,它对我有用。
  • @AaronMcDaid [temp.param]/14 "模板 template-parametertemplate-parameter 允许有一个默认 模板参数。”但是,这不会影响模板是否是有效的模板模板参数(它并不是很有用,live example)。
  • 在我的实验中,在 g++-4.6.3 上,do_something 中指定的任何默认值都优先于Foo 中指定的任何默认值。这有点奇怪,即使它是标准的,我也不喜欢它。有没有一种更简洁的方法来制作单参数模板,它是双参数默认模板的别名。 AFAIK,C++11 中的新 using 模板别名可能是相关的。

标签: c++ templates


【解决方案1】:

模板参数的参数忽略默认参数。在 n3337,第 [temp.arg.template] 章,第 2 段中有这个例子:

template<class T> class A { /∗ ... ∗/ };
template<class T, class U = T> class B { /∗ ... ∗/ };
template <class ... Types> class C { /∗ ... ∗/ };
template<template<class> class P> class X { /∗ ... ∗/ };
template<template<class ...> class Q> class Y { /∗ ... ∗/ };
X<A> xa; // OK
X<B> xb; // ill-formed: default arguments for the parameters of a template argument are ignored
X<C> xc; // ill-formed: a template parameter pack does not match a template parameter
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK

请注意上面X&lt;B&gt; xb; 的评论。恐怕我找不到规范文本。

您可以将其与函数相关联——默认参数也不是签名的一部分。如果你试图调用一个通过函数指针默认参数的函数,也会发生同样的事情。

【讨论】:

  • 但显然您可以将默认参数放在模板模板参数中
  • 是的,显然 - 但这与评论所说的不同。
【解决方案2】:

使用 using 模板别名(C++11 中的一项新功能),您可以创建一个单参数模板,该模板等效于另一个具有两个参数的模板,其中一个是默认参数。

template <E e> using Foo1 = Foo<e>;

这将创建Foo1,一个单参数模板,尽管Foo 在技术上具有两个参数,其中一个是默认的。您可以将其用作:

do_something<Foo1>(int(55));

或者,如果 using 等 C++11 功能不可用,则可以在 do_something 的声明中指定默认值。不幸的是,这意味着do_something 不能再处理简单的单参数模板。因此,我认为上面的using 方法更好。

template <template <E, class = void> class Action, class T>
void do_something(const T& value);

如果您采用这种方法,将 args 中的默认值设置为 do_something,则此默认值优先于声明 Foo 中指定的默认值。这是基于我的实验,我无法自信地评论什么是标准,什么不是标准。但我确实认为 using 技巧完全符合 C++11 的标准。

(Ubuntu clang 版本 3.0-6ubuntu3)

【讨论】:

    猜你喜欢
    • 2018-07-16
    • 1970-01-01
    • 2015-08-15
    • 2013-10-01
    • 2021-12-27
    • 1970-01-01
    • 2013-02-28
    • 1970-01-01
    相关资源
    最近更新 更多