8

は、次のコードを考えてみましょう:C++の過負荷解決ルールの欠陥?

#include <iostream> 

namespace ns1 
{ 
    struct A 
    { 
    }; 

    template <class T> 
    std::ostream& operator << (std::ostream& os, const T& t) 
    { 
     return os << "ns1::print" << std::endl; 
    } 
} 

namespace ns2 
{ 
    template <class T> 
    std::ostream& operator << (std::ostream& os, const T& t) 
    { 
     return os << "ns2::print" << std::endl; 
    } 

    void f (const ns1::A& a) 
    { 
     std::cout << a; 
    } 
} 


int main() 
{ 
    ns1::A a; 

    ns2::f (a); 

    return 0; 
} 

コンパイルは標準どおり「あいまいな過負荷エラー」で失敗します。

なぜですか?確かにAの「ホーム」ネームスペースの「同等に良い」演算子が優先されるべきですか?それをしない論理的な理由はありますか?

+8

なぜ 'A 'の「ホーム」名前空間の関数が、呼び出し関数fの名前空間の関数よりも優先されるべきだと思いますか?これをあいまいにする方法はありません。エラーは唯一の賢明なことです。 –

+0

名前空間より作成した人は、Aをどのように印刷するべきかが分かりますか? – cppalex

+8

まず、テンプレートです。 「A」を作成した人が、タイプAのオブジェクトを印刷するための特定の動作を保証したい場合、それらは過負荷または特殊化のいずれかを提供していたであろう。それはここのあいまいさを解決したでしょう。第二に、名前空間は複数回開いたり閉じたりすることができるので、関数はAの実装者によって提供されていなくてもよい。 –

答えて

10

namespace Aのオーバーロードを優先する場合は、実際にはそれを改善するために何かを追加する必要があります。ないテンプレートをそれを作ることで、言う:

namespace ns1 
{ 
    std::ostream& operator<<(std::ostream&, const A&); 
} 

それ以外の場合は、両者が全く同じであれば1、名前空間内の関数テンプレートは、別の名前空間に関数テンプレートに好まれる理由を参照するには何の概念的な理由は本当にありません。結局のところ、Aの名前空間の関数テンプレートは、fの名前空間の関数テンプレートよりも "良い"のはなぜですか? fの実装者は「よく知っていますか?機能の署名のみに依拠することは、この問題を回避します。

0

あなたは慎重にコンパイラエラーを読めば、曖昧エラーがns1ns2ではなく、ns1からoperator<<(os, const char*)インスタンス化とnamespace stdからまったく同じ過負荷の間operator<<バージョン間ではありません。後者はADLによってstd::ostreamにドラッグされています。

最善のアプローチは、@Barryによって推薦を使用することで、デテンプレート化名前空間ns1operator<<だけでなく、同じ名前空間に(例えばf(A)など)ns1::Aに関連するすべての機能を追加する:

#include <iostream> 

namespace ns1 
{ 
    struct A {}; 

    std::ostream& operator << (std::ostream& os, const A& t) 
    { 
     return os << "ns1::print" << std::endl; 
    } 

    void f (const A& a) 
    { 
     std::cout << a; 
    }  
} 

int main() 
{ 
    ns1::A a; 
    f(a); // rely on ADL to find ns1::operator<<(os, A) 
} 

Live Example

ネームスペースns1は次にADLを通してclass Aの広いインタフェースとして作用します。

+1

はい、いいえ。あなたが文字列をSFINAEした場合、 'cout << a'は2つの関数テンプレートのあいまいさが残っています – Barry

+0

私はOPの例を簡略化して' operator <<(std :: ostream&、const T& ) 'と' g(const T&) 'のようになります。 – Barry

+0

@Barry 'print()'何かこのような驚きを排除するでしょう。 – TemplateRex

関連する問題