There was a question regarding passing taking the following function template, and instantiating it with a by-reference string parameter:
template <typename T>
void foo(T t) {}
The answer was, of course, to give the argument explicitly:
int main() {
std::string str("some huge text");
foo<std::string&>(str);
}
(As an aside, there was a suggestion about using deduction and passing C++0x’s std::ref(str), but this requires the use of .get() inside the function, and the OP’s requirement was transparency.)
However, IMO there can be little doubt that the author of the function template intended for the argument to be passed by value, or he would have written:
template <typename T>
void foo(T& t) {}
(It’s possible that he deliberately intended for either to be possible, but this seems unlikely to me for some reason.)
-
Are there any “reasonable” scenarios where passing a reference into
foo<T>, where the author offoohad intended the function always to take its argument by value, may cause problems? -
The answer I’m looking for most likely consists of a function body for
foo, where the use of a reference type forTleads to unexpected/undesirable semantics when compared to the use of a value type forT.
Consider that it’s common to write algorithms like this:
It’s also how the standard algorithms are implemented both in GCC and in the old SGI STL.
If
firstis taken by reference then it will be modified, so certainly there are plenty of template functions out there that will “go wrong” with reference template arguments. In this example,firstis changed to a value equal tolast. In another implementation, or in the next release, it might be copied and not modified at all. That’s “unexpected/undesirable semantics”.Whether you call this a “problem” or not, I’m not sure. It’s not the behavior that the author of the function intended, and it’s probably not documented what happens to
firstif it’s passed by reference (it isn’t for the standard algorithms).For the standard algorithms I think it’s UB, so the caller is at fault. The standard says that the template argument should be an iterator, and while
T*or some library iterator is an iterator type,T* &or reference-to-library-iterator isn’t.As long as authors of template functions have documented the requirements of their template arguments clearly, I suspect that typically it will just fall out in the same way as for standard algorithms and iterators — reference types are not valid template arguments, and hence the caller is at fault. In cases where the requirements are very simple (a couple of expressions with specified behavior), a reference type probably isn’t ruled out, but again as long as the function doesn’t say that it doesn’t modify the argument, and doesn’t say how it modifies the argument, callers should consider that since it’s not documented how the argument is modified, then it’s unspecified. If they call the function with a reference type, and get surprised whether or how the argument is modified, again it’s the caller’s fault.
I expect that under-documented functions are at risk of a dispute whose fault it is when it goes wrong, though, since sometimes it’ll rely on quite a close reading.