1

列挙型に基づいて演算子をオーバーロードしたいクラスがあるとしましょう:

#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++ によると、メインからオペレーターへの呼び出しは無効です。

error: no match for ‘operator<’ (operand types are ‘&lt;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 は正しく、定義の形式が正しくありませんか?

4

1 に答える 1

2

私は、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は membercoutではなくグローバルです。

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;
}

サンプルはこちら。g++ も上記のコードに満足しています。


非推定コンテキストでの演算子に関する注意。ここでのコードは何らかの大きなプロジェクトで使用していると思いますが、演算子が推定されないパラメーターで使用されている場合は、多くの場合、メンバー メソッドまたはフリー関数 ( friendasを使用) で機能を実装する方が簡単で明確です。必要)。

于 2016-01-28T11:19:31.227 に答える