重载运算符'<<'的显式专门化(左移)

时间:2016-01-28 11:00:48

标签: c++ templates operator-overloading explicit-specialization

假设我有一个类,我想基于枚举类型重载运算符:

#include <iostream>

enum class option : char { normal, do_something_stupid };

class foo
{
public:

    int i;    
    explicit foo(int a=0) : i(a) {};
    /* overload operator '+=' based on 'option' */
    template<option E = option::normal>
    void operator+=(const foo& f) { i += f.i; }
};

/* explicit specialization for operator += */
template<> void foo::operator+=<option::do_something_stupid>(const foo& f)
{ i += (f.i +1000); }

int main()
{
    foo f1(1), f2(2);
    f1 += f2;
    std::cout << "\nf1 = " << f1.i;
    f1.operator+=<option::do_something_stupid>(f2);
    std::cout << "\nf1 = " << f1.i;

    std::cout << "\n";
    return 0;
}

这在g ++和clang ++上构建了clean(忽略了它确实做了非常好的事情)。

如果我想重载'&lt;&lt;&lt;操作员同样的方式?类似的方法似乎不起作用:

#include <ostream>
#include <iostream>

enum class option : char { normal, do_something_stupid };

class foo
{
public:

    int i;

    explicit foo(int a=0) : i(a) {};

    template<option E = option::normal>
    friend std::ostream& operator<<(std::ostream& o, const foo& f)
    { o << f.i; return o; }
};

template<> std::ostream&
operator<< <option::do_something_stupid>(std::ostream& o, const foo& f)
{ 
    o << f.i + 1000;
    return o;
}

int main()
{
    foo f1(1), f2(2);

    std::cout << "\nf1= " << f1;
    std::cout << "\nf2= ";
    /* this triggers an error with g++ */
    std::cout.operator<< <option::do_something_stupid>(f1);

    std::cout << "\n";
    return 0;
}

根据g ++,从main到operator的调用无效:

error: no match for ‘operator<’ (operand types are ‘<unresolved overloaded function type>’ and ‘option’)
std::cout.operator<< <option::do_something_stupid>(f1);
另一方面,

clang ++会产生不同的错误消息:

lsfov.cc:20:1: error: 'operator<<' cannot be the name of a variable or data member
operator<< <option::do_something_stupid>(std::ostream& o, const foo& f)
^
lsfov.cc:20:11: error: expected ';' at end of declaration
operator<< <option::do_something_stupid>(std::ostream& o, const foo& f)
          ^
          ;
lsfov.cc:20:12: error: expected unqualified-id
operator<< <option::do_something_stupid>(std::ostream& o, const foo& f)
           ^
lsfov.cc:33:15: error: reference to non-static member function must be called
    std::cout.operator<< <option::do_something_stupid>(f1);
    ~~~~~~~~~~^~~~~~~~~~

继续列出'&lt;&lt;'的可能重载来自标准库(如果我理解正确的话),例如:

/usr/bin/../lib/gcc/x86_64-redhat-linux/5.3.1/../../../../include/c++/5.3.1/ostream:108:7: note: possible target for call
      operator<<(__ostream_type& (*__pf)(__ostream_type&))
      ^
/usr/bin/../lib/gcc/x86_64-redhat-linux/5.3.1/../../../../include/c++/5.3.1/ostream:117:7: note: possible target for call
      operator<<(__ios_type& (*__pf)(__ios_type&))
      ^

发生了什么事?这种运营商专业化是否可行/允许?如果是这样,调用运营商的正确方法是什么?或者说铿锵正确,定义是否形成错误?

1 个答案:

答案 0 :(得分:2)

我认为clang不喜欢# uname -a Linux 259307ada273 3.10.0-229.el7.x86_64 #1 SMP Fri Mar 6 11:36:42 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux 关于专业化的声明。重新订购它们就可以了。

friend

此外,enum class option : char { normal, do_something_stupid }; // forward declare the class and operator class foo; template<option E = option::normal> std::ostream& operator<<(std::ostream& o, const foo& f); // the class with the declared friend operator class foo { private: int i; public: explicit foo(int a=0) : i(a) {}; template<option E> friend std::ostream& operator<<(std::ostream& o, const foo& f); }; // the operator implementations template<option E> std::ostream& operator<<(std::ostream& o, const foo& f) { o << f.i; return o; } template<> std::ostream& operator<< <option::do_something_stupid>(std::ostream& o, const foo& f) { o << f.i + 1000; return o; } 中使用的operator<<不是成员main,而是全球。

cout

Sample here。 g ++也很满意上面的代码。

关于非推断上下文中的运算符的注释。我假设您在某个更大的项目中使用此处的代码,但如果运算符与非推导参数一起使用,则在成员方法或自由函数中实现该功能通常更容易和更清晰(使用{根据需要{1}}。