【问题标题】:Can I instantiate a template without repeating its signature?我可以在不重复签名的情况下实例化模板吗?
【发布时间】:2015-02-05 23:20:28
【问题描述】:

我想用长签名实例化一些函数:

template<typename T> void foo(
    T& t,
    SomeType some_parameter,
    AnotherType another_parameter,
    EtcType yet_another_parameter,
    AsYouCanTell this_is_a_very_long_signature);

实例化foo 的直接方法是:

template void foo<int>(
    int& t,
    SomeType some_parameter,
    AnotherType another_parameter,
    EtcType yet_another_parameter,
    AsYouCanTell this_is_a_very_long_signature);

但这是长签名的重复。如果我想要 5 种不同类型的特定实例化怎么办——我要复制 5 次吗?没道理……

我在想也许我可以写

template decltype(foo<int>);

但由于某种原因,这不起作用。我可以让它工作吗?

【问题讨论】:

  • 他不想做任何这些事情。他正在尝试显式实例化此模板。
  • 你不需要函数参数的名称,顺便说一句:template foo&lt;int&gt;(int&amp;, SomeType, AnotherType, EtcType, AsYouCanTell);。您可以使用类型别名进一步缩短类型名称。
  • 我想知道您是否可以滥用 C++14 的变量模板来执行此操作:与其显式实例化函数模板,不如显式实例化引用函数模板的变量模板可能就足够了。可能,使用静态数据成员和别名模板也可以实现类似的效果,但我不确定。

标签: c++ templates c++11 instantiation decltype


【解决方案1】:

确实,您可以在不重复其签名的情况下实例化您的函数 - 但语法有点不同:

template
decltype(foo<int>) foo<int>;

decltype 给你一个 type 但显式实例化需要一个 declaration 这是一个类型后跟一个名称。

尝试使用 GCC 4.9.1;即使使用-pedantic 标志,它也可以按预期工作并且编译时没有任何警告。

【讨论】:

  • @dyp 我不是语言律师。我承认我发现这个解决方案主要是偶然的,并且很高兴 GCC 接受了它。不过,我很想听听标准对此有何评论。
  • 如果您没有提供实现,则此答案会提供预期的编译器错误:(clang 3.5.0)error: explicit instantiation of undefined function template 'foo'。我曾尝试过我自己的另一种解决方案,但在缺少实现时它只会给我链接器错误。
  • 我不愿意说这是完全合法的,因为[dcl.fct]/12 "函数类型的typedef可以用来声明一个函数,但不能用来定义一个函数。” 但我认为虽然答案中的陈述是一个显式实例化定义,但我认为它不算作函数 wrt [dcl.fct] 的定义/12.
  • @dyp 那条规则交叉引用了[dcl.fct.def],而[dcl.fct.def.general]/p1中的语法基本上说它必须有body或者=default;或者=delete; 算作函数​​定义。
  • 如果有另一个不相关的template&lt;typename&gt; void foo(args) 与另一个签名(cf Mooing Duck's argument)会发生什么?
【解决方案2】:

它实际上比@5gon12eder 建议的更简单:

template decltype(foo<int>) foo;

但是是的,就像他说的那样——decltype() 只提供类型,而签名并不是真正的类型。

编辑:当模板有值参数而不仅仅是类型时,这不起作用,所以如果我们有

template <typename T, unsigned Val> bar(T t);

然后

template decltype(bar<int, 1>) bar;

不会编译,而

template decltype(bar<int, 1>) bar<int, 1>;

会的。

【讨论】:

  • 我很惊讶这行得通。这是否意味着为第二个foo 推断出类型?太酷了。
  • @dyp 但它必须被编译为正确模板实例化的错误名称,否则链接器找不到定义。我查看了汇编代码,无论是否提供了&lt;int&gt;,它的读数完全相同。
  • @dyp 模板参数显然必须从类型中推导出来。
  • @Columbo 我目前不确定这是否合法。但据我所知,这遵循奇怪的using my_func_t = void(int); my_func_t my_func_name; 语法。
  • @5gon12eder 再想一想,你是对的:foo 的模板参数是从函数参数的类型推导出来的(通过decltype(foo&lt;int&gt;) 指定)。这类似于template void foo&lt;int&gt;(int);template void foo(int);。前者也适用于template void foo&lt;int&gt;(); 的情况,不需要从函数参数类型中推导。
【解决方案3】:

不,因为超载

template<typename T> void foo(T& t,
                     SomeType some_parameter,
                     AnotherType another_parameter,
                     EtcType yet_another_parameter,
                     AsYouCanTell this_is_a_very_long_signature);
template<template T> void foo(T& t); //completely unrelated function
template<template T> void foo(char); //another completely unrelated function

现在想象一下,显式实例化第一个所需的最少信息是多少?好吧,你需要完整的签名来消除歧义,所以

explicit int foo(int&, SomeType, AnotherType, EtcType, AsYouCanTell)

是理论上的最小信息量。所以 C++ 需要的实际上开销很小:

template void foo<int>(int& t, SomeType, AnotherType, EtcType, AsYouCanTell);

如果您不想输入所有内容,那么 Konrad 建议的宏是可行的方法。

【讨论】:

  • 但是我没有这些重载函数。
  • 该语言必须使用一种格式进行显式实例化,该格式无论存在什么其他函数都是有效的。它们是否真的存在是无关紧要的。它们可能存在,并且语法必须支持它。
【解决方案4】:

我认为这是一个很好的、合法的宏用途:

#define INSTANTIATE_FOO(type) \
    template void foo<type>(type& t, \
                  SomeType some_parameter, \
                  AnotherType another_parameter, \
                  EtcType yet_another_parameter, \
                  AsYouCanTell this_is_a_very_long_signature);

INSTANTIATE_FOO(int)
INSTANTIATE_FOO(float)
INSTANTIATE_FOO(my_little_dragon)

#undef INSTANTIATE_FOO

【讨论】:

  • 在定义宏时,您仍然至少要复制一次签名。如果你试图避免重复,它就会变成一个复杂的宏......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-26
  • 2021-03-01
  • 1970-01-01
  • 2011-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多