2013-06-16 13 views
19

ref-qualified関数のオーバーロードで作業している間に、Clang(2.9とトランク)とは異なる結果になります。次のコードを考えてみましょう:ref-qualifierでのオーバーロードの解決

#include <iostream> 
#include <utility> 

struct foo 
{ 
    int& bar() & 
    { 
     std::cout << "non-const lvalue" << std::endl; 
     return _bar; 
    } 
    //~ int&& bar() && 
    //~ { 
    //~  std::cout << "non-const rvalue" << std::endl; 
    //~  return std::move(_bar); 
    //~ } 
    int const& bar() const & 
    { 
     std::cout << "const lvalue" << std::endl; 
     return _bar; 
    } 
    int const&& bar() const && 
    { 
     std::cout << "const rvalue" << std::endl; 
     return std::move(_bar); 
    } 

    int _bar; 
}; 

int main(int argc, char** argv) 
{ 
    foo().bar(); 
} 

をクランGCCが、これは2 const修飾の機能が最良の実行可能な候補者であることの両方を持つあいまいな呼び出しであると考えている間、その出力"const rvalue"をコンパイルします。 4つのオーバーロードをすべて提供すると、両方のコンパイラは"non-const rvalue"を出力します。

- 何でも -が正しいことをしているのか、そして関連する標準のものが何であるか知りたいのですが。

注:これは実際に重要理由は、実際のコードがconstexprとして、両方のconst修飾関数を宣言していることです。もちろん、std::moveの代わりにstd::coutstatic_castへの出力がないので、有効なconstexprの定義になります。 のC++ 11constexprにはまだconstが含まれているので、サンプルコードでコメントアウトされたオーバーロードは、const修飾されたrvalueオーバーロードを再定義するため、提供できません。

答えて

27

まず、暗黙オブジェクトパラメータが13.3.1.4の通り、通常のパラメータとして扱われる:非静的メンバ関数について

、暗黙のオブジェクトパラメータの種類は

ある - 「左辺値& & REF-修飾子で宣言された関数のCV Xの右辺値参照 『 - 「REF-修飾子なしで、または& REF-修飾子

と宣言された関数は、』 CV Xへの参照

ここで、Xは関数がメンバであるクラスであり、cvはメンバ 関数宣言のcv-qualificationです。

だから、何を求めていることは、次のと同等です:

void bar(foo&); 
void bar(foo&&); 
void bar(const foo&); 
void bar(const foo&&); 

int main() 
{ 
    bar(foo()); 
} 

表現foo()がクラスprvalueです。

第2に、非定数左辺参照バージョンは実行できません。これは、右辺値がそれにバインドできないためです。

これは、過負荷解決のための3つの実行可能な機能を残します。

それぞれに暗黙的なオブジェクトパラメータ(const foo&,foo&&またはconst foo&&)が1つあります。したがって、これらの3つをランク付けして最適な一致を判断する必要があります。

すべての3つの場合において、が直接参照結合であるである。これは、宣言子/初期化(8.5.3)で説明されています。

13.3.3.2.3に記載されている三つの可能なバインディング(const foo&foo&&及びconst foo&&)のランキング:

標準変換シーケンスS1は、標準的な変換シーケンスよりも良好な変換シーケンスであるS2場合

  • は、S1とS2は、参照バインディングであり、どちらもこの例外は、ここでは適用されない[REF-修飾子なしで宣言、非静的メンバ関数の暗黙的なオブジェクトパラメータを意味しません彼らはすべてref-qualifierを持っています] S1は右辺値に右値参照をバインドします [クラスprvalueは右値です] とS2は左辺値参照をバインドします。

これはfoo&&const foo&&両方がconst foo&その後、優れていることを意味しています。

  • S1及びS2は、参照バインディングであり、参照が参照するタイプは、トップレベルのCV-修飾子を除いて同じタイプであり、基準S2によって初期化するタイプが多い指しS1によって初期化された参照がを参照する型よりもcv修飾されています。

これはfoo&&const foo&&よりも優れていることを意味しています。

だからClangが正しいとGCCのバグです。

struct foo 
{ 
    int&& bar() &&;    // VIABLE - BEST (1) 
    int const&& bar() const &&; // VIABLE -  (2) 
    int const& bar() const &; // VIABLE - WORST (3) 
    int& bar() &;    // NOT VIABLE 

    int _bar; 
}; 

GCCのバグは(ref-qualifiersで)暗黙のオブジェクトパラメータに純粋に適用するように思われる通常のパラメータには、少なくとも4.7で、正しい順位を取得しているようだ、次のようにfoo().bar()のランキング過負荷があります。 2。

+0

すべての場合において、正の値の参照がよりよく一致します。これはおそらくGGCのref-qualifierのバグです。 – Xeo

+0

また、[この回答](http://stackoverflow.com/a/8610728/500104)が適切かもしれません。 ;) – Xeo

+0

詳細な回答ありがとうございます。私はそれを何回もupvoteできればいいと思うよ! –

関連する問題