卡住了---用C ++创建一个运算符模板

时间:2015-02-11 00:40:14

标签: c++ templates operators

我正在创建一个模仿cout的类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;
                       ^

2 个答案:

答案 0 :(得分:0)

错误消息的关键部分是&#34;模板参数扣除/替换失败&#34;。 endl不是一个对象,它甚至不是一个函数。它是一个函数 template ,它是一个&#34; cookie cutter&#34;用于生成无限数量的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;
}

答案 1 :(得分:0)

您的代码失败只是因为std::endl是函数模板,因此编译器需要知道您要使用的模板的实例化。标准IOStream类对操纵器具有单独的重载,并且它们显式指定模板实例化。你也可以这样做:

SpyOutput& operator<<(std::ostream& (*manip)(std::ostream&))
{
    if (os) manip(*os);
    return *this;
}

现在当你执行spy << std::endl时,这将实例化有效的std::endl<char, std::char_traits<char>>,代码将起作用。虽然通常我不会重新创建整个流类,而是使用std::streambuf接口。