如何避免为类模板指定参数,而使用默认模板参数。

218
如果我被允许做以下事情:
template <typename T = int>
class Foo{
};

为什么我不能在主函数中执行以下操作?
Foo me;

但是我必须明确以下内容:
Foo<int> me;

C++11引入了默认模板参数,但目前它们对我完全理解起来有些难以捉摸。
6个回答

266

注意:

C++17版本中,不带模板参数的Foo me;是合法的。请参见此答案:https://dev59.com/02Up5IYBdhLWcg3wS2LP#50970942

适用于C++17之前的原始答案:

您需要执行以下操作:

Foo<> me;

模板参数必须存在,但您可以将它们留空。

可以将其视为具有单个默认参数的函数foo。表达式foo不会调用它,但是foo()会调用它。参数语法仍然必须存在。这是一致的。


6
如果Foo可能是模板标识符或显式实例化取决于是否有默认参数,那么使用它可能会产生一些不必要的复杂性。最好保留显式实例化语法。可以将其视为具有单个默认参数的函数foo。你不能像调用foo一样调用它,而应该使用foo()进行调用。保持这种一致性是有意义的。 - Joseph Mansfield
3
但是您不能像foo那样不带参数调用函数;但是您可以将没有参数的类命名为Foo - Seth Carnegie
4
通过函数,可以从函数参数中推导出模板参数。但是通过类,无法确定你是想要一个带有默认参数的模板类还是一个非模板类。 - Olaf Dietsche
25
但是你不能同时拥有同名的模板类和非模板类,因此编译器只需查看名称即可决定。 - Seth Carnegie
7
@Pubby 标准委员会也曾经有过同样的疑问。现在,随着C++17的到来,在这种情况下不再需要使用<>了。请查看我的答案以获取更多详细信息。 - Paolo M
显示剩余3条评论

113

使用C++17,你确实可以。

这个功能被称为类模板参数推导,它增加了声明模板类型变量的灵活性。

因此,

template <typename T = int>
class Foo{};

int main() {
    Foo f;
}

现在合法的 C++ 代码


6
奇怪。我在我的C ++17项目中尝试了它,但它不起作用:“模板占位符类型'const MyType'必须跟随一个简单的声明符ID”。我正在使用GCC 7.3.0。 - Silicomancer
1
@Silicomancer 如果没有看到您的代码和命令行,很难说...也许您正在处理指针就像这里 - Paolo M
1
Clang好像不接受?http://coliru.stacked-crooked.com/a/c5d3c0f90ed263c2 - Borgleader
2
@PaoloM 哦,很酷,很高兴知道这只是编译器版本问题。感谢您的关注。 - Borgleader
5
这应该是最佳答案 - 最佳答案已过时。 - drew.neely
显示剩余3条评论

31

您不被允许那样做,但您可以这样做。

typedef Foo<> Fooo;

然后执行

Fooo me;

这里有一个默认类型和 typedef Foo<float> Fooo; 之间的区别吗? - qrikko
12
C++11 的写法是 using Fooo = Foo<>;。这行代码的意思是将 Fooo 定义为一个不带参数的 Foo 类型的别名。 - Adrian W

22

您可以使用以下内容:

Foo<> me;

使用int作为您的模板参数。角括号是必需的,不能省略。


明白了,谢谢。但是,如下所述,为什么类型说明必须存在呢? - user633658
@user633658:你是指“类型说明符”吗?我不太明白。 - Andy Prowl
无论如何,关于需要空的角括号的原因,我只能做出猜测,它们都是为了排除仅使用模板名称可能存在的歧义,但我必须承认我不知道确切的原因。 - Andy Prowl
我强烈怀疑<>的要求是为了使编译器的解析器能够确定您正在引用一个名为foo的模板类,而不是其他东西称为foo。 - Mac

0

有点不同的情况,而且比较晚,但涉及到模板函数gcc 11.2好像无法编译这个:

template <typename T = int>
struct Test {};
template<typename T> void foo(T& bar) {}
int main()
{
    Test t;
    foo<Test>(t);
}

但是没有问题

template <typename T = int>
struct Test {};
template<typename T> void foo(T& bar) {}
int main()
{
    Test t;
    foo<Test<>>(t);
}

当然

template <typename T = int>
struct Test {};
template<typename T> void foo(T& bar) {}
int main()
{
    Test t;
    foo(t);
}

工作 - 但有时需要明确强制类型。这是编译器的错误吗?


请注意,这取决于编译器版本-您的第三个示例在C++14中无法运行,但在C++17中可以正常工作。这似乎是由C++语言规范而非编译器错误导致的。具体来说,如果您要部分指定模板(例如 foo<Test>(t)),则需要为每个默认模板提供<>-但仅输入foo(t)即可正常工作。 - Ethan McTague
答案提供了额外的信息,但似乎是推测性的或部分是一个问题本身。 - Ethan McTague
引发这个“可疑答案”的实际使用需要模板参数。在这种情况下,它不是部分特化。我认为这里实际上是相同的情况 - 它是完全特化的。我相信还有另一个原因导致这无法编译(但感谢您的评论)。 - Radrich
我提出了这个问题:https://dev59.com/V8Tra4cB1Zd3GeqPvg2H - Radrich
如果您有新的问题,请通过单击提问按钮来提出。如果它有助于提供上下文,请包含此问题的链接。- 来自审核 - Sneftel

-1
根据C++17标准,模板参数需要传递。
但是如果你仍然想绕过这一点,你可以像这样使用using关键字。
template <typename T>
class Foo{
};
using IFoo=Foo<int>

或者,你也可以这样使用预处理器

template <typename T>
class Foo{
};

#define IFoo Foo<int>

快速提醒

预处理器对调试不利。


2
这比使用IFoo=Foo<int>更糟糕。宏会忽略命名空间。 - MSalters

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接