我能否在不重复其签名的情况下实例化一个模板?

4

我想实例化一些带有较长参数列表的函数:

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>);

但由于某些原因,这并不起作用。我能以某种方式使其起作用吗?


1
他并不想做那些事情。他试图显式实例化这个模板。 - Puppy
1
顺便说一下,您不需要函数参数的名称:template foo<int>(int&, SomeType, AnotherType, EtcType, AsYouCanTell);。您可以通过使用类型别名进一步缩短类型名称。 - dyp
我想知道你是否可以滥用C++14的变量模板来实现这个功能:可能不需要显式地实例化函数模板,而是显式实例化引用函数模板的变量模板就足够了。可能,使用静态数据成员和别名模板也可以实现类似的事情,但我对此不确定。 - dyp
4个回答

9

您确实可以实例化函数而不重复其签名-但语法有点不同:

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

decltype可以给你一个类型,但是显式实例化需要一个声明,这个声明包括一个类型和一个名称。

在GCC 4.9.1上尝试过,它按预期工作并编译不会出现任何警告,即使使用了-pedantic标志。


@dyp 我不是语言律师。我承认我大多数是偶然发现了这个解决方案,并且很高兴GCC接受了它。但我很想听听标准对此的看法。 - 5gon12eder
这个答案提供了预期的编译器错误,如果您没有提供实现:(clang 3.5.0)error: explicit instantiation of undefined function template 'foo'。我尝试过另一种解决方案,但是当实现缺失时,它只会给我链接器错误。 - Aaron McDaid
1
我不愿意说这完全是合法的,因为[dcl.fct]/12中指出:“函数类型的typedef可以用于声明函数,但不得用于定义函数。”但我认为,尽管答案中的语句是一个显式实例化定义,但我认为它并不符合[dcl.fct]/12关于函数定义的规定。 - dyp
2
@dyp 这个规则交叉参考了[dcl.fct.def],而[dcl.fct.def.general]/p1中的语法基本上表明它必须有一个函数体或=default;=delete;才能算作函数定义。 - T.C.
如果有另一个与之无关的 template<typename> void foo(args) 并带有另一种签名(参见Mooing Duck的论点),那会发生什么? - Walter
这适用于gcc和clang,可惜不适用于MSVC:https://godbolt.org/z/Gb7vaa3We - undefined

4
实际上,这比@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>;

将。

1
我很惊讶这个能运行。这是不是意味着第二个 foo 的类型被推断了?太酷了。 - 5gon12eder
@dyp 但它必须被编译为正确模板实例化的摩尔斯名称,否则链接器将找不到定义。我已经查看了汇编代码,无论是否提供<int>,它都读取完全相同。 - 5gon12eder
@dyp 模板参数显然必须从类型中推导出来。 - Columbo
@Columbo,我目前不确定这是否合法。但据我所知,这遵循奇怪的“using my_func_t = void(int); my_func_t my_func_name;”语法。 - dyp
1
@5gon12eder 再次思考,你是对的:foo 的模板参数是从函数参数的类型中推导出来的(这些类型是通过 decltype(foo<int>) 指定的)。这类似于 template void foo<int>(int);template void foo(int);。前者也适用于 template void foo<int>(); 这种情况,其中不需要从函数参数类型中推导。 - dyp
显示剩余2条评论

1
我认为这是宏的一个很好且合法的应用场景:
#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

你仍然至少要在定义宏时重复一次签名。如果你试图避免这种重复,宏就会变得非常复杂... - einpoklum

1
不,因为负载过重。
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的建议是使用宏的方法。

但是我没有任何这些重载函数。 - einpoklum
该语言必须使用一种明确实例化的格式,无论存在哪些其他函数都应该是有效的。它们是否实际存在并不重要。它们可能存在,语法必须支持这一点。 - Mooing Duck

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