在不使用<>的情况下调用模板函数;类型推断

18
如果我有一个带有typename T的函数模板,编译器可以自动设置类型,那么当我调用此函数时,不必明确写出该类型,例如:
template < typename T > 
T min( T v1, T v2 ) {
   return ( v1 < v2 ) ? v1: v2;
}
int i1 = 1, i2 = 2; int i3 = min( i1, i2 ); //no explicit <type> 

但是如果我有一个包含两个不同类型名称的函数模板,例如:

template < typename TOut, typename TIn >
TOut round( TIn v ) {
   return (TOut)( v + 0.5 );
}
double d = 1.54;
int i = round<int>(d); //explicit <int>

我需要确认一下,是否总是必须至少指定一个类型名?我猜测原因是因为C++无法区分不同返回类型的函数。

但如果我使用一个void函数并传递引用,那么我不必显式指定返回类型:

template < typename TOut, typename TIn > 
void round( TOut & vret, TIn vin ) {
   vret = (TOut)(vin + 0.5);
}
   double d = 1.54;
   int i; round(i, d); //no explicit <int>

在编写模板时,结论应该避免使用带返回值的函数,更倾向于使用通过引用返回的void函数吗?或者有可能避免明确编写返回类型吗?类似于模板中的“类型推断”。C++0x中是否支持“类型推断”?


类型之间的转换使类型推断变得难以控制,因此您不能在返回类型上进行重载,并且必须在它是模板参数时指定它。 - Francesco
你可能需要改进你的四舍五入算法。-1.54应该输出什么?如果你想得到一个四舍五入的double值呢? - UncleBens
3个回答

22

重载决议仅基于函数参数执行;返回值根本不被使用。如果无法根据参数确定返回类型,则必须显式指定。

我不会选择通过引用参数“返回”值的方式,因为这使得调用代码不清晰。例如,我更喜欢这种写法:

double x = round<double>(y);

关于这个问题:

double x;
round(x, y);

因为在后一种情况下,容易混淆输入和输出,并且很难确定x是否被修改。

特别是在round的特定情况下,您可能只需要一个或两个类型来用于TOut,因此可以省略该模板参数:

template<typename TIn>
int roundToInt(TIn v) {
    return (int)(v + 0.5);
}

我认为roundToInt(x)round<int>(x)更清晰,因为它明确了int类型的用途。


3
关于清晰度:round_to<int>(x)?;) - UncleBens
@UncleBens,我认为roundToInt(x)roundTo<int>(x)更清晰,后者比round<int>(x)明显更清晰 - Gabriel Staples
话虽如此,如果您想保持编写模板函数一次的好处,而不是为要舍入到的每种输出类型编写单独的函数,同时仍然可以将其舍入到多种类型,则roundTo<int>(x)确实是一个很好的替代方案。但在大多数情况下,您只需要舍入到几种类型时,我认为roundToInt(x)的显式性比让某人舍入到任何东西更好,因此,除非我需要让某人舍入到实际上任何类型,否则我会选择roundToInt(x)(加上定义一两个遵循这种模式的函数)。 - Gabriel Staples

3
避免使用带返回值的函数,更倾向于使用通过引用返回的void函数来编写模板。这样做可以获得什么好处呢?只有类型推断(因此要写的代码更少)。但是您会失去更加逻辑的语法赋值(因此需要编写更多的代码)。所以一方面得到了好处,另一方面失去了好处。我总体上看不到好处。在某些情况下,明确指定模板类型甚至可能会有帮助:考虑lexical_cast的情况。不指定返回的模板类型将会令人困惑。

2

我想补充一下其他人所说的,建议您优先使用C++类型转换而不是C风格的类型转换。

vret = (TOut)(vin + 0.5);

对比

vret = static_cast<TOut>(vin + 0.5);

如果您尝试转换不相关的类型,static cast将总是失败。这有助于调试。


而且你也会在编译时收到警告/错误,这比在运行时发现要好得多。 - Matthieu M.
在这个模板中,涉及的类型必须是什么,才能使static_cast产生与C风格转换不同的结果?(是的,在实用函数中,您可能会追求良好的风格,但我认为在执行数字类型之间的转换的纯数学代码中,没有太多好处 - 不能保证一篇文章,而不是也许只是一条评论。) - UncleBens
1
你可能已经看腻了这篇文章,但我还是想插一句。如果你使用 static_cast,那么错误意味着对象/可执行文件甚至无法存在;反之亦然,也就是说,如果你有一个对象/可执行文件,你知道 static_cast 在类型之间是有效的。随着你深入模板元编程,你将需要更多地控制它们继承树中的类型,例如在 CRTP 中。 - John P

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