关于传递采用以下函数模板有a question,使用按引用字符串参数实例化它:
template <typename T>
void foo(T t) {}
答案当然是明确提出论点:
int main() {
std::string str("some huge text");
foo<std::string&>(str);
}
(另外,有一个关于使用演绎和传递C ++ 0x std::ref(str)
的建议,但这需要在函数内部使用.get()
,并且OP的要求是透明度。)
然而,IMO毫无疑问函数模板的作者打算通过值传递参数,或者他会写:
template <typename T>
void foo(T& t) {}
(他有可能故意让两者成为可能,但出于某种原因这似乎不太可能。)
是否有任何“合理”的情况,其中将引用传递到foo<T>
,其中foo
的作者意图该函数始终通过价值,可能会引起问题吗?
我正在寻找的答案很可能包含foo
的函数体,其中使用T
的引用类型会导致意外/不合需要的语义使用T
的值类型。
答案 0 :(得分:7)
如果程序员决定是懒惰的,并使用输入参数作为临时变量来计算输出,那么你会得到有问题的结果:
template <class T>
T power2n(T a, int n) {
if (n == 0) return 1;
for (int i = 0 ; i < n; i++)
{
a *= a;
}
return a;
}
现在,如果我通过引用传递第一个参数,它的值就会搞砸了。
不是说这是好的编程,只是说它发生了。
答案 1 :(得分:7)
考虑编写这样的算法很常见:
template <typename InputIterator>
void dosomething(InputIterator first, InputIterator last, otherparams) {
while (first != last) {
do the work;
++first;
}
}
这也是GCC和旧SGI STL中标准算法的实现方式。
如果引用first
,那么它将被修改,所以当然有很多模板函数会出现参考模板参数“出错”。在此示例中,first
更改为等于last
的值。在另一个实现中,或者在下一个版本中,它可能被复制而根本不被修改。这是“意外/不受欢迎的语义”。
你是否称之为“问题”,我不确定。这不是函数作者所期望的行为,并且可能没有记录first
如果它通过引用传递会发生什么(它不是标准算法)。
对于标准算法,我认为它是UB,因此调用者有错。标准说模板参数应该是迭代器,而T*
或某些库迭代器是迭代器类型,T* &
或引用到库 - 迭代器不是。
只要模板函数的作者清楚地记录了他们的模板参数的要求,我怀疑通常它将以与标准算法和迭代器相同的方式出现 - 引用类型不是有效的模板参数,因此调用者有错。在需求非常简单的情况下(一些具有指定行为的表达式),可能不会排除引用类型,但只要该函数没有说它不修改参数,并没有说 它如何修改参数,调用者应该考虑因为没有记录参数是如何被修改的,所以它是未指定的。如果他们用引用类型调用函数,并且对参数是否或如何被修改感到惊讶,那么再次是调用者的错误。
我认为,记录不足的函数存在争议的风险,但是当它出错时会出现错误,因为有时它会依赖于非常接近的读数。