2015-10-15 10 views
24

g ++の下でコンパイルするコードはありますが、clangではコンパイルできません。これは有効なC++ 11

2つの名前空間宣言のマージなど、さまざまな小さな方法で変更した場合、コードがコンパイルされます。

clang -fsyntax-only -std=c++11 testcase.cpp 

を用いてコンパイルするG ++:

g++ -fsyntax-only -std=c++11 testcase.cpp 

バージョンは、両方のUbuntuのコア15.04に、G ++ 4.9.2打ち鳴らす3.6.0ある。打ち鳴らすと共にコンパイルする

// The problem disappears without namespaces. 
namespace Root { 
    // The problem disappears if 'g' is in the global namespace, and we change 
    // the friend declaration to '::g' 

    // The problem disappears if 'g' has void return type. 

    // The problem disappears if we get rid of the 'Value' template argument 
    // and the 'value' parameter. 
    template<typename Value, typename Defaulted = void> 
    bool g(Value value); 

    // The problem disappears if MyClass is not a template. 
    template<typename ClassValue> 
    class MyClass { 
    private: 
     template<typename Value, typename Defaulted> 
     friend bool g(Value value); 
    }; 
} 

// The problem disappears if we declare the Root namespace in a single block 
// containing 'g', 'MyClass' and 'f'. 

// The problem remains if we declare f in the global namespace and reference 
// Root::g. 
namespace Root { 
    void f() { 
     MyClass<int> value; 

     g(value); 
    } 
} 

私はこれが打ち鳴らすのバグであると考えてい

testcase.cpp:24:9: error: no matching function for call to 'g' 
     g(value); 
     ^
testcase.cpp:14:21: note: candidate template ignored: couldn't infer template argument 'Defaulted' 
     friend bool g(Value value); 
       ^
1 error generated. 
+0

フレンド関数は(クラスで宣言されていても)名前空間スコープを持っています。この場合、 'bool Root :: g()'の2つの関数宣言があります。テンプレートパラメータは宣言を変更しません。だからあなたのマイナーな変更がこのコードを動作させる理由です。実際にはg ++でコンパイルするともっと驚いています。 –

+3

@SimonKraemerあなたは関数を複数回宣言することができます。とにかく 'friend'宣言は関数を再宣言しません。 – Barry

+0

これは完全なコードですか、それとも関連する部分ですか? –

答えて

7

クランは、エラーメッセージを表示します。 [temp.param]、我々が持っている:

フレンド関数テンプレート宣言がデフォルトテンプレート引数を 指定した場合、その宣言は、定義しなければならないと 関数テンプレート内の宣言のみでなければなりません翻訳単位。使用可能

デフォルトのセットテンプレート引数は、デフォルトの関数の引数は、(8.3.6)であるのと同じ方法で、テンプレートの 全て前の宣言からデフォルト引数をマージすることによって得られます。

template <typename T, typename U=int> 
void h(); 

template <typename T, typename U> 
void h() { } 

h<int>(); 

をそして、これは完全によくコンパイルを打ち鳴らすコードを形成している。

後者の点は、私たちが書くことができることを意味します。我々gが以前に宣言されているように、引用されたルールに基づいてgに、デフォルトのテンプレート引数を指定して、まだマージステップを経由してvoidとして使用するためのDefaultedが利用可能に維持する必要があり、それを指定するないないすることができます。 default-argumentが利用可能な場合、ルックアップは我々が望むgを見つけることができるはずです。

回避策は、単に友人になり、我々は気に専門:

friend bool g<>(MyClass value); 
+0

@ T.Cへのカウントダウン。 5 ... 4で私を訂正する... – Barry

+0

ありがとうございます - 合併についてあなたが言っていることに同意します、それは私には意味があります。しかし、私はあなたがテンプレートのparamがデフォルトで何を指定しても、友人の中で何も指定できないと思っていました。それで、最初のパラグラフの私の(誤った)解釈は次のようになります:すでに既定値(別の場所で宣言されている)を持つテンプレートパラメータを持つ友達を宣言することによって、関数宣言をfriend宣言と一緒に提供する必要があります。 –

+0

もっと考えた後、私は今理解しています。私が気づいていなかった友人テンプレートのデフォルトを指定することができ、それは私を混乱させました。だからこれは非常にclangのバグのように見えます。 –

関連する問題