2016-10-18 8 views
3

私は奇妙な演算子を持っています< <問題がありますが、その理由はわかりません。次のコードは問題の抽出です。このコードは、VS2015、g ++ 5.4.0、およびclang 3.8.0を使用してコンパイルできないため、コンパイラのバグではありません。演算子<<オーバーロードが他のものを隠す

#include <iostream> 
#include <stdexcept> 

inline std::ostream &operator<<(std::ostream &o, const std::exception &ex) { 
    o << ex.what(); 
    return o; 
} 

namespace ns { 
    struct Struct { int m; }; 

    inline std::ostream &operator<<(std::ostream &o, const Struct &s) { 
     o << s.m; 
     return o; 
    } 

    void fn() { 
     std::cout << Struct{ 1 } << std::endl; 
     try { 
      throw std::runtime_error("..."); 
     } catch (std::exception &ex) { 
      std::cout << ex << std::endl; 
     } 
    } 
} 

int main() { 
    return 0; 
} 

コンパイラは、STDのためのオペレータ< <の過負荷を見つけることができません::例外(ライン "のstd :: coutを< <元< <のstd ::てendl;" 失敗しました)。私はグローバル名前空間のコードがコンパイルさ

に名前空間NSからすべてのコードを移動した場合

  • がオーバーロード演算子構造体の< <または
  • を削除します。どちらかのI場合はどう特に私を混乱することは、あります。この行動の理由は何ですか?

+0

あなたはコンパイラのメッセージやエラーを投稿するのを止めましたか? – Nawaz

+1

@Nawaz:何がOPを停止したのか分かりませんが、1つの理由は、GCCで今試したときのエラーメッセージが202行と16799文字であったという理由があります。 Bjarneが彼のオーバーロードされた暗黙の空白は承認を得ることができなかったが、C++は確かにそのエラーメッセージで私たちを荒しするために管理しています... –

+0

はああ申し訳ありません:VS2015は述べています:**重大\tコード\t説明\tプロジェクト\tファイル\tライン\t抑制状態 エラー\t C2679 \tバイナリ '<<': 'std :: exception'(または受け入れ可能な変換はありません)タイプの右オペランドをとる演算子は見つかりません**。 –

答えて

3

あなたが見ているのは、C++の名前検索ルールの結果です。このため、記述したクラス型のオペランドが少なくとも1つもない場合に、演算子オーバーロードを提供することはお勧めしません。

つまり、operator<<(std::ostream&, std::exception)がないようにコードを設計する必要があります。誤って別のoperator<<によって隠されることがあります。

この動作はすべての機能で同じですが、オペレータ名は特殊なケースではありません。

代わりの解決方法は、using ::operator<<;を、多くのオーバーロードを定義する任意のネームスペースの中に入れてoperator<<とすることです。


名前ルックアップ規則の完全なセットは少し複雑です。非修飾関数名検索の要点は、

  1. argument-dependent lookupです。
  2. 非ADLルックアップで名前が検索されます。
  3. (1)と(2)の結果を組み合わせてルックアップセットを生成します。あなたのコードo << s.m、ステップ

非会員operator<<するための部材でoperator<<、および名前空間stdを探索クラスstd::ostream

ステップ2検索:

  • あなたはその関数内の任意の関数宣言があった場合にns::operator<<のボディ、! (何も見つかりません)
  • 名前空間ns、呼び出しが発生する関数のポイントまでです。

キーポイントはns::operator<<が非ADL検索によって発見されると、非ADL検索プロセスが停止することである(ns::operator<<を発見) - それはより多くのオーバーロードを見つけるために、さらに囲む名前空間を検索する続行されません。

最終的なルックアップセットは、(1)と(2)の和集合、つまりstd::ostream::operator<<,std::operator<<、およびns::operator<<です。その後、過負荷の解決は、それらの修飾された名前のオーバーロードでのみ行われます。ここで


は、同じ原理の単純な例であるADLの気晴らしは、で追加された設定せずに、:

void bar(int); 

namespace N 
{ 
    void bar(char const *); 

    void nfunc() { bar(1); } 
} 

引数1がクラス型でないため、何のADLはありません。非ADL非修飾ルックアップはN::barであり、停止します。 ::barは考慮されません。コードがコンパイルに失敗します。

+1

[Koenig lookup](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup)は参考になるでしょうか? –

+0

ありがとう、これは私が仕事できるものです:-)。 –

+0

@AdrianColomitchiが私の答えにcppreferenceリンクを追加しました –

関連する問題