我不确定这段代码是否会编译。
我正在使用的示例代码:
#include <iostream>
using std::cout;
using std::endl;
class Foo {
public:
template<typename T>
Foo& operator<<(const T& t) {
cout << t;
return *this;
}
};
int main() {
Foo foo;
foo << "Hello World"; // perfectly fine
foo << endl; // shit hits the fan
return 0;
}
这是错误:
test.cpp:19:12: error: no match for ‘operator<<’ in ‘foo << std::endl’
test.cpp:19:12: note: candidates are:
test.cpp:10:14: note: template<class T> Foo& Foo::operator<<(const T&)
test.cpp:10:14: note: template argument deduction/substitution failed:
test.cpp:19:12: note: couldn't deduce template parameter ‘T’
我很困惑为什么它不能用endl
(ostream& (*)(ostream&)
)的函数类型代替T
,当你指定{{1}时,它显然可以做到这一点}}
我发现另外令人费解的是,这解决了问题[编辑]
cout << endl;
如果问题不明确,我会问为什么它不能首先推断出模板。
答案 0 :(得分:4)
endl
是一个操纵者,即它是未解析的函数类型。有几个重载,类型推断无法决定你想要哪一个。
更具体地说,这是endl
的样子(在GNU libc ++中):
/**
* @brief Write a newline and flush the stream.
*
* This manipulator is often mistakenly used when a simple newline is
* desired, leading to poor buffering performance. See
* http://gcc.gnu.org/onlinedocs/libstdc++/manual/bk01pt11ch25s02.html
* for more on this subject.
*/
template<typename _CharT, typename _Traits>
inline basic_ostream<_CharT, _Traits>&
endl(basic_ostream<_CharT, _Traits>& __os)
{ return flush(__os.put(__os.widen('\n'))); }
已更新因此,问题是,编译器无法推断您要传递的 endl
实例(它是未解决的超载)。 您可以通过static_cast<ostream&(*)(ostream&)>(endl)
来解决此问题。
当然,这不方便。这是一个简单的修复:http://liveworkspace.org/code/2F2VHe$1
#include <iostream>
using std::cout;
using std::endl;
class Foo : public std::ostream
{
public:
template<typename T>
Foo& operator<<(T&& t) {
cout << std::forward<T>(t);
return *this;
}
typedef std::ostream& (manip)(std::ostream&);
Foo& operator<<(manip& m) {
cout << m;
return *this;
}
};
int main() {
Foo foo;
foo << "Hello World"; // perfectly fine
foo << endl; // everything is fine
return 0;
}
答案 1 :(得分:2)
问题是endl
是一个定义为功能模板的操纵器。 C ++ 11标准的第27.7.1段规定了其签名:
template <class charT, class traits>
basic_ostream<charT,traits>& endl(basic_ostream<charT,traits>& os);
template <class charT, class traits>
此外,根据第13.3.1段关于重载决议:
在候选者是函数模板的每种情况下,使用模板参数推导生成候选函数模板特化(14.8.3,14.8.2)。然后以通常的方式将这些候选人作为候选职能处理。
您的operator <<
被定义为模板,编译器需要推导出T
的类型。但是,编译器如何知道您所指的endl
实例化?如何推断模板参数charT
和traits
?您致operator <<
的电话中没有其他内容可以从中推断出来。
你有两种解决这个问题的方法。要么明确地转换endl
的类型,要告诉编译器应该选择哪个重载:
foo << (std::ostream& (*)(std::ostream&))endl;
或者,正如您所做的那样,您创建了一个operator <<
的重载,它接受具有该特定签名的函数。您的编译器现在将选择它:
Foo& operator<<(ostream& (*f)(ostream&))
{
return *this << f;
}
在这个函数定义中,f
是什么没有歧义:它的类型是精确定义的。但是,请注意:此功能不太可能达到预期效果!事实上,它只是一直在调用自己,产生无限递归!
因此,这个断言:
[...]注意我实际上正在调用我的其他方法实现:
不正确:您没有调用其他方法实现,而是一遍又一遍地调用相同的函数。