调用不带 <> 的模板函数;类型推断

Posted

技术标签:

【中文标题】调用不带 <> 的模板函数;类型推断【英文标题】:Calling template function without <>; type inference 【发布时间】:2011-02-19 11:53:51 【问题描述】:

如果我有一个带有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>

我总是必须指定至少 1 个类型名是真的吗?我认为原因是因为 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 中是否可以进行“类型推断”?

【问题讨论】:

类型之间的转换使类型推断的想法变得笨拙,因此您不能重载返回类型,并且必须在它是模板参数时指定它。 您可能想要处理您的舍入算法。 -1.54 应该是什么?并且:如果你想得到一个四舍五入的 double 值怎么办? 【参考方案1】:

仅基于函数参数完成重载解析;根本不使用返回值。如果无法根据参数确定返回类型,则必须显式指定它。

我不会走通过引用参数“返回”值的路径;这使得调用代码不清楚。例如,我更喜欢这个:

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&lt;int&gt;(x) 更清晰一点,因为很清楚int 类型的用途。

【讨论】:

关于清晰度:round_to&lt;int&gt;(x)? ;) @UncleBens,我想说roundToInt(x)roundTo&lt;int&gt;(x) 更清晰,而round&lt;int&gt;(x)round&lt;int&gt;(x)清晰得多 话虽如此,roundTo&lt;int&gt;(x) 当然是一个不错的选择,如果您想保持一次编写模板函数的好处,而不是为每个输出类型编写一个单独的函数来四舍五入,同时仍然获得对多种类型进行四舍五入的能力。但是,在大多数情况下,无论如何你只四舍五入,我认为roundToInt(x) 的明确性比让某人四舍五入要好,所以我会选择roundToInt(x)(另外定义一两个以下这种模式)除非我需要让某人四舍五入到几乎任何类型。【参考方案2】:

结论是在编写模板时避免使用返回函数,更喜欢通过引用返回的 void 函数

不,为什么?你有什么收获?仅类型推断(因此编写的代码更少)。但是您失去了更合乎逻辑的赋值语法(因此需要编写更多代码)。所以一件事得到,另一件事失去。我没有看到总体上的好处。

甚至帮助必须明确指定模板类型:考虑lexical_cast 的情况。不指定返回模板类型会令人困惑。

【讨论】:

【参考方案3】:

让我补充一下其他人所说的话,你应该更喜欢 C++ 转换而不是 C 样式转换。

vret = (TOut)(vin + 0.5);

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

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

【讨论】:

你也会有编译时警告/错误,比在运行时发现要好得多。 所涉及的类型必须是什么,以便static_cast 会产生与此模板中的 C 样式转换不同的结果? (是的,在实用程序函数中,您可能会努力追求良好的风格,但我认为纯数学代码在数字类型之间进行转换并没有多大好处 - 保证发布,而不是评论。) 你可能已经结束了这篇文章,但我不得不插话。如果你使用static_cast,错误意味着对象/可执行文件甚至不存在;那么反之亦然,也就是说,如果你有一个对象/可执行文件,你就知道static_cast 在类型之间是有效的。随着您进一步进入模板元编程,您将需要对其继承树中的类型进行更多控制,例如在 CRTP 中。

以上是关于调用不带 <> 的模板函数;类型推断的主要内容,如果未能解决你的问题,请参考以下文章

模板与泛型编程——模板实参推断

推断拥有成员函数的类的类型

模板参数推导

类模板

推断指针非类型模板参数的类型

C++ Primer 5th笔记(chap 16 模板和泛型编程)模板实参推断