2016-04-12 7 views
6

C++ 11の専門家にはいくつか質問があります。SFINAEとSFINAEでg ++とclang ++の動作が異なる

異なる動作を(3.5.0)++私はSFINAEと戦っていると私はグラムで奇妙なケースに出くわし++(4.9.2)、および打ち鳴らします。

私は、次のサンプルコードを用意しました。申し訳ありませんが、私はそれをもっと簡潔にすることができません。

#include <string> 
#include <iostream> 
#include <typeinfo> 
#include <type_traits> 

template <typename X> 
class foo 
{ 
    private: 
     template <typename R> 
     using enableIfIsInt 
     = typename std::enable_if<std::is_same<X, int>::value, R>::type; 

    public: 
     foo() 
     { } 

     template <typename R = void> 
     enableIfIsInt<R> bar() 
      { std::cout << "bar: is int\n"; } 

     void bar() 
     { 
     std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{" 
      << typeid(enableIfIsInt<void>).name() << "}\n"; 
     } 
}; 


int main() 
{ 
    foo<long> fl; 
    foo<int> fi; 

    fl.bar(); 
    fi.bar(); 

    return 0; 
} 

私の考えでは(SFINAE経由)というテンプレートfoo<X>クラスを作成することでしたXテンプレート引数に応じて、1つまたは別の方法でメソッドを定義することができます。誰右:プログラムはG ++ 4.9.2が、打ち鳴らすとうまくコンパイル

++ 3.5.0は、次のエラー

test.cpp:13:36: error: no type named 'type' in 
     'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable 
     this declaration 
     = typename std::enable_if<std::is_same<X, int>::value, R>::type; 
           ^~~~~~~~~~~~~~~~~~~~~~~~~~~ 
test.cpp:26:23: note: in instantiation of template type 
     alias 'enableIfIsInt' requested here 
      << typeid(enableIfIsInt<void>).name() << "}\n"; 
        ^
test.cpp:36:7: note: in instantiation of member function 
     'foo<long>::bar' requested here 
    fl.bar(); 
    ^
1 error generated. 

を与える私は右打ち鳴らす++が、C++ 11人の専門家への私の最初の質問がされていると仮定しますか? g ++またはclang ++?

グラム++生成プログラムの出力について、それがfl.bar();命令を無視しているようだ++以下

bar: isn't int; is [i]{v} 

そうグラムです。

今はほとんど変化:私はこの方法で機能の醜態内std::enable_ifを削除

void bar() 
    { std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n"; } 

foo<X>::bar()の第二のバージョンを変更します。今、両方のG ++と打ち鳴らす++問題なくコンパイルされ、出力は、プログラムの両方のコンパイルバージョンの、だから、

bar: isn't int; is [l] 
bar: isn't int; is [i] 

で、私の2番目の質問です:私が間違ってやっていますか?なぜ、intの場合、の"is int"バージョンが得られませんか?私はC++ 11を学ぶためにしようとしている:私はいくつかの愚かをやっている場合

は私と一緒に我慢して。

そして、私の悪い英語のため申し訳ありません。

+1

は、あなたがより多くの最近の打ち鳴らすのバージョン(のような3.8)でこれを試してみましたし、より最近のgcc(5.3など)?教えてください... –

+0

ほとんど最小化されたrepro:http://coliru.stacked-crooked.com/a/713fbbbca7e8b8c5。私はキャッシングを責める。 –

+2

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70642 –

答えて

6

打ち鳴らすのエラーは、置換の障害から来ていません。それはここから来ています:

void bar() 
    { 
    std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{" 
     << typeid(enableIfIsInt<void>).name() << "}\n"; // <== 
    } 

enableIfIsInt<void>は即時コンテキストではありませんが、それはXのためのハード障害がintはないです。その文脈でその式を使うことはできません。あなたはそれを削除すると

- 非テンプレートbar()は常に呼ばれています。これは、両方の機能が同等の一致であり、過負荷解決のテンプレートよりも非テンプレートが優先されるからです。

だから、真の解決策は、タグ・ディスパッチを使用することです:

void bar() { bar(std::is_same<X, int>{}); } 

void bar(std::true_type) { 
    std::cout << "bar: is int\n"; 
} 

void bar(std::false_type) { 
    std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n"; 
} 

喜んで両方のコンパイラ歩留まりれる:

bar: isn't int; is [l] 
bar: is int 
+0

私が焦点を当てていなかったのは、非テンプレートが過負荷解決のテンプレートよりも優先されるということでした。ありがとう。 – max66

関連する問題