stuck --- 在 C++ 中创建运算符模板
stuck --- Creating an Operator Template in C++
我正在创建一个模仿 cout 的 class SpyOutput,我正在尝试使用一个模板,这样我就不必重载 << 运算符 4 次(每种数据类型一次):
#include <iostream>
#include <sstream>
using namespace std;
class SpyOutput
{
ostream *os;
stringstream ss;
int sum, temp;
public:
SpyOutput(ostream *s):os(s), sum(0){}
template <class T>
SpyOutput& operator<<(T x)
{
ss << x;
*os << x;
return *this;
}
};
int main(void)
{
SpyOutput spy(&cout);
spy << "Hello" << endl;
spy << "1235" << endl;
spy << 'z' << endl;
spy << 4.56 << endl;
return 0;
}
我无法编译它,它似乎无法识别我的模板。有任何想法吗? G++ 错误消息是
main.cpp: In function 'int main()':
main.cpp:24:20: error: no match for 'operator<<' (operand types are 'SpyOutput' and '<unresolved overloaded function type>')
spy << "Hello" << endl;
^
main.cpp:24:20: note: candidates are:
main.cpp:13:16: note: template<class T> SpyOutput& SpyOutput::operator<<(T)
SpyOutput& operator<<(T x)
^
main.cpp:13:16: note: template argument deduction/substitution failed:
main.cpp:24:23: note: couldn't deduce template parameter 'T'
spy << "Hello" << endl;
^
错误信息的关键部分是"template argument deduction/substitution failed"。 endl
不是对象,甚至不是函数。它是一个函数 template,这是一个 "cookie cutter" 用于生成无限数量的 cookie 函数。当 <<
到 ostream
时,编译器会查看是否有任何潜在的 <<
函数可以阐明模板参数应该是什么。并且存在这个过载:
ostream& operator<< (ostream& (*pf)(ostream&));
当编译器检查此重载时,它意识到它可以明确地将 endl
特化为 ostream& (*)(ostream&)
,因此选择此重载并执行匹配流类型的隐式特化。幸运的是,您所要做的就是提供上述函数重载,希望这个魔法对您也有用。
请注意,ostreams 还有两个重要的重载作为您必须添加的成员:
ostream& operator<< (ios& (*pf)(ios&));
ostream& operator<< (ios_base& (*pf)(ios_base&));
还值得一提的是,您的函数正在尝试复制您流式传输的所有对象,这可能会导致它失败或行为异常。一个更明智的想法是使用通用引用。或者在 至少 通过 const 引用捕获。
//if T is a template, then T&& is a universal reference. A perfect match for everything
//if T were not a template, it would be an rvalue reference. Totally unrelated.
template <class T> SpyOutput& operator<<(T&& x) {
ss << x;
*os << x;
return *this;
}
您的代码失败仅仅是因为 std::endl
是一个函数 template,因此编译器需要知道您要使用模板的哪个实例化。标准 IOStream classes 有单独的操纵器重载,它们明确指定模板实例化。你也可以这样做:
SpyOutput& operator<<(std::ostream& (*manip)(std::ostream&))
{
if (os) manip(*os);
return *this;
}
现在,当您执行 spy << std::endl
时,这将实例化 std::endl<char, std::char_traits<char>>
,这是有效的,代码将起作用。虽然一般来说我不会重新创建整个流 class 而是使用 std::streambuf
接口。
我正在创建一个模仿 cout 的 class SpyOutput,我正在尝试使用一个模板,这样我就不必重载 << 运算符 4 次(每种数据类型一次):
#include <iostream>
#include <sstream>
using namespace std;
class SpyOutput
{
ostream *os;
stringstream ss;
int sum, temp;
public:
SpyOutput(ostream *s):os(s), sum(0){}
template <class T>
SpyOutput& operator<<(T x)
{
ss << x;
*os << x;
return *this;
}
};
int main(void)
{
SpyOutput spy(&cout);
spy << "Hello" << endl;
spy << "1235" << endl;
spy << 'z' << endl;
spy << 4.56 << endl;
return 0;
}
我无法编译它,它似乎无法识别我的模板。有任何想法吗? G++ 错误消息是
main.cpp: In function 'int main()':
main.cpp:24:20: error: no match for 'operator<<' (operand types are 'SpyOutput' and '<unresolved overloaded function type>')
spy << "Hello" << endl;
^
main.cpp:24:20: note: candidates are:
main.cpp:13:16: note: template<class T> SpyOutput& SpyOutput::operator<<(T)
SpyOutput& operator<<(T x)
^
main.cpp:13:16: note: template argument deduction/substitution failed:
main.cpp:24:23: note: couldn't deduce template parameter 'T'
spy << "Hello" << endl;
^
错误信息的关键部分是"template argument deduction/substitution failed"。 endl
不是对象,甚至不是函数。它是一个函数 template,这是一个 "cookie cutter" 用于生成无限数量的 cookie 函数。当 <<
到 ostream
时,编译器会查看是否有任何潜在的 <<
函数可以阐明模板参数应该是什么。并且存在这个过载:
ostream& operator<< (ostream& (*pf)(ostream&));
当编译器检查此重载时,它意识到它可以明确地将 endl
特化为 ostream& (*)(ostream&)
,因此选择此重载并执行匹配流类型的隐式特化。幸运的是,您所要做的就是提供上述函数重载,希望这个魔法对您也有用。
请注意,ostreams 还有两个重要的重载作为您必须添加的成员:
ostream& operator<< (ios& (*pf)(ios&));
ostream& operator<< (ios_base& (*pf)(ios_base&));
还值得一提的是,您的函数正在尝试复制您流式传输的所有对象,这可能会导致它失败或行为异常。一个更明智的想法是使用通用引用。或者在 至少 通过 const 引用捕获。
//if T is a template, then T&& is a universal reference. A perfect match for everything
//if T were not a template, it would be an rvalue reference. Totally unrelated.
template <class T> SpyOutput& operator<<(T&& x) {
ss << x;
*os << x;
return *this;
}
您的代码失败仅仅是因为 std::endl
是一个函数 template,因此编译器需要知道您要使用模板的哪个实例化。标准 IOStream classes 有单独的操纵器重载,它们明确指定模板实例化。你也可以这样做:
SpyOutput& operator<<(std::ostream& (*manip)(std::ostream&))
{
if (os) manip(*os);
return *this;
}
现在,当您执行 spy << std::endl
时,这将实例化 std::endl<char, std::char_traits<char>>
,这是有效的,代码将起作用。虽然一般来说我不会重新创建整个流 class 而是使用 std::streambuf
接口。