c++ - 重载运算符的显式特化 '<<'(左移)

标签 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++ 上构建得很干净(忽略它确实做了一些漂亮的转储的事实)。

如果我想以同样的方式重载“<<”运算符怎么办?类似的方法似乎不起作用:

#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);
    ~~~~~~~~~~^~~~~~~~~~

继续列出标准库中“<<”的可能重载(如果我理解正确的话),例如:

/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&))
      ^

这是怎么回事?这种运营商特化可能/允许吗?如果是这样,调用接线员的正确方法是什么?或者 clang 是否正确并且定义格式不正确?

最佳答案

我认为 clang 不喜欢与特化相关的 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,而是一个全局的。

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

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

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

Sample here 。 g++ 也对上面的代码感到满意。


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

关于c++ - 重载运算符的显式特化 '<<'(左移),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35059612/

相关文章:

c++ - 在C/C++中读取特殊列,同时避免文本文件中的某些特殊字符

html - 如何在不覆盖现有 CSS 的情况下包含多个 CSS?

c# - 重载 + 运算符,因此它对在已检查或未检查的上下文中被调用很敏感

c++ - 根据SSE特性调用不同的函数实现

c++ - 使用字符串选择模板类的特化

c++ - 可变参数模板运算符<<

c++ - 返回与。指针

c++ - 程序崩溃并出现 'std::out_of_range' 错误

C++ 在函数中创建新对象

c++ - “使用类模板需要模板参数”错误,并带有默认指定的非类型模板参数值