2015-10-28 12 views
9

で実行している、(from here)私はg++で実行ないながら、次のコードは、clang++で実行されていることがわかった:可変引数テンプレート:グラムとエラー++が、可変引数テンプレート、クラス、関数やラムダで遊んでいる間打ち鳴らす++

g++についてはcoliru

clangについては

#include <iostream> 
#include <string> 
using namespace std; 
template <class... F> 
struct overload_set : F... 
{ 
    overload_set(F... f) : F(f)... {} 
}; 
template <class... F> 
auto overload(F... f) 
{ 
    return overload_set<F...>(f...); 
} 
int main() 
{ 
    auto func = overload (
     [](int &val) { val *= 2; }, 
     [](string &arg) { arg += arg; }, 
     [](char &c) { c = 'x'; } 
     ); 
    int val = 10; 
    string str = "stackoverflow"; 
    char ch = 's'; 
    cout << val << " : " << str << " : " << ch << endl; 
    func(val); 
    func(str); 
    func(ch); 
    cout << val << " : " << str << " : " << ch << endl; 
    return 0; 
} 

coliru

g++は、func(val),func(str)およびfunc(c)の場合、あいまいな数字を示しています(operator())。私はoperator()がそれぞれ異なる引数を持っているのであいまいであってはならないと思う。

g++の問題点を教えてください。

+1

@Niall:それは本当にラムダやバリデーションテンプレートではありません。異なる 'operator()(X&)'関数を持つ 'B1'、' B2' ...から派生する最も単純な 'struct'を与えられても、g ++は' B1 :: operator();を使う必要があります。 B2 :: operator(); 'を使用します([here](http://coliru.stacked-crooked.com/a/8bc933418b5ff82f)参照)。 –

+1

@TonyD。私はそれがclangバグだと思っています - いくつかのテストは、clangが基本クラスから関数を解決する方法と、呼び出し演算子を解決する方法との違いがあることを示しています。 – Niall

+0

@Niall gotcha、clangは 'x()'と 'x.operator()()'を別々に解決します。 –

答えて

3

これは、lambdas、variadicテンプレート、演算子、または高度なC++ 1 {xy}のものとはほとんど関係がありません。のは、単純化してみましょう:

struct foo 
{ 
    void func(int&){} 
}; 
struct bar 
{ 
    void func(char&){} 
}; 

struct test : foo, bar {}; 

int main() 
{ 
    test t; 
    int i = 1; 
    char c = 'a'; 
    t.func(i); 
    t.func(c); 
} 

これはg++clang++のいずれかでコンパイルに失敗しました。これは言語がどのように動作するように指定されているので、どちらも良いことです。オペレータが呼び出された方法に応じ

我々はoperator()funcを変更する場合は、g++はプログラムを拒否し続けたがclang++、それを受け入れるか拒否のいずれか、:

t.operator()(c); // rejected 
t(c);   // accepted 

私に打ち鳴らすのバグのように見えます。

コンパイル上記のコードを作成するためには、非常に小さな変更が必要とされている:

struct test : foo, bar { 
    using foo::func; 
    using bar::func; 
}; 

は今、私が使用してディレクティブでパック拡張作業を行うための方法が分からない、またはそれが実際に可能かどう。

template <class... F> struct overload_set; 

template <> struct overload_set<> {}; 

template <class F> struct overload_set<F> : F { 
    using F::operator(); 
    overload_set(F f) : F(f) {} 
}; 

template <class F, class... Fs> 
struct overload_set<F, Fs...> : F, overload_set<Fs...> 
{ 
    overload_set(F f, Fs... fs) : F(f), overload_set<Fs...>(fs...) {} 
    using F::operator(); 
    using overload_set<Fs...>::operator(); 
}; 

この変更により、コードがg++clang++の両方でコンパイルします。しかし、回避策があります。

+2

OPのリンクされたブログ記事のリンクがありますが、これはほぼこの回避策です。ブログでは、これを「産業用バージョン」(https://github.com/picanumber/CODEine/blob/master/lambda_utils.h) – Niall

+0

@Niallとして紹介しています。産業用バージョンは私が他の人の中でYakkを見ました(ベーステンプレート、特殊化の数、完璧なフォワーディングのバリエーションがあります) –

関連する問題