2016-04-04 13 views
13

私はより良いタイトルを見つけることができませんでしたが、あなたが正しい考えを持っている場合は、それを自由に変更してください。そのままで、それはよりも優れています。とにかくGCC vs clangです。Variadicテンプレートと関数のポインタ:どのコンパイラが正しいのですか?


私はこのコードで間違っているかを把握しようとしている:

template <typename... T> 
struct S; 

template <typename T, typename... U> 
struct S<T, U...>: S<U...> { 
    using S<U...>::f; 

    template<void(*F)(const T &)> 
    void f() { } 
}; 

template<> 
struct S<> { 
    void f(); 
}; 

template<typename... T> 
struct R: S<T...> { 
    using S<T...>::f; 

    template<typename U, void(*F)(const U &)> 
    void g() { 
     this->template f<F>(); 
    } 
}; 

void h(const double &) { } 

int main() { 
    R<int, double> r; 
    r.g<double, h>(); 
} 

それはGCC 4.9hereを参照)でコンパイルが、それは打ち鳴らす3.8でコンパイルされません。 .here参照)。

正しいコンパイラとその理由は何ですか?
さらに、両方のコンパイラでコンパイルされたコードを見るにはどうすればよいですか?

+0

それは(まだテスト)奇妙だと今の私は疑問にフラグ「可変引数-テンプレート」を追加提案することができます。 – Radek

+0

ICCでもこれをコンパイルできないようです:https://godbolt.org/g/N67e2z – NathanOliver

+0

[この質問に関連するかもしれません](http://stackoverflow.com/questions/8629839/c-candidate-template-ignored -invalid-explicit-specified-for-templ)または[この質問](http://stackoverflow.com/questions/33872039/invalid-explicitly-specified-argument-in-clang-but-successful-compilation-in -gcc)? – callyalater

答えて

9

ここではclangが正しいと思いますが、これはgccのバグです。宣言を使用しては、派生クラス、メンバ関数と メンバ関数テンプレートに、基本クラスからの宣言をもたらした場合

[namespace.udecl]から

struct U { 
    template<int > void foo() { } 
}; 

struct X : U { 
    using U::foo; 
    template<void* > void foo() { } 
}; 

int main() { 
    X{}.foo<1>(); // gcc ok, clang error 
} 

:まずは、簡単な例から始めましょう派生クラスのメンバー関数とメンバ関数をオーバーライドまたは非表示にする ベースクラスの同じ名前、パラメータタイプリスト(8.3.5)、cv-qualification、およびref-qualifier(存在する場合)を持つテンプレート紛争よりむしろ)。そのような隠蔽された宣言またはオーバーライドされた宣言は、宣言によって導入された 宣言のセットから除外されます。 、CV-資格(なし)、及びREF修飾子(なし);

U::fooX::fooは、同じ名前、パラメータ型リスト(なし&ダガー)を有します。したがって、X::fooは、オーバーロードするのではなくU::fooを隠します。間違ったタイプを間違いなくX::fooに渡しています。単に別の関数ポインタ型でのオーバーロード(というよりもテンプレートパラメータとしてそれらを取る)

が正常に動作します:

template <typename T, typename... U> 
struct S<T, U...> : S<U...> { 
    using S<U...>::f; 

    void f(void (*F)(const T&)) { } 
}; 

それとも、本当にテンプレート引数として関数ポインタが必要な場合は、まだでそれらをラップすることにより、過負荷になる可能性がありタグタイプ:

template <class T> 
using fptr = void(*)(const T&); 

template <class T, fptr<T> F> 
using func_constant = std::integral_constant<fptr<T>, F>; 

て、それを介して伝播:

template <typename T, typename... U> 
struct S<T, U...>: S<U...> { 
    using S<U...>::f; 

    template <fptr<T> F> 
    void f(func_constant<T, F>) { } 
}; 

と:

template <class U, fptr<U> F> 
void g() { 
    this->f(func_constant<U, F>{}); 
} 

&短剣。parameter-type-listの定義にはテンプレートパラメータは記載されていません。@barry後

+0

しかし、parameter-type-listは、テンプレート宣言から来たパラメータパックについて言及しています。それで、テンプレートがオーバーロードと隠蔽のための関数解決で考慮されることを意味しないのですか? – callyalater

+0

@callyalaterしかし、パラメータパックは引き続きパラメータです。テンプレートの引数はありません。 – Barry

+0

@ Barryタグの一種ですね。私は同じと思ったが、それが最も適切な解決策であるかどうか疑問に思っていた。詳細な応答をありがとう(いつものように、本当に感謝します)。 – skypjack

3

- にS専門にfの定義を変更:

template <typename T, typename... U> 
struct S<T, U...>: S<U...> { 
    using S<U...>::f; 


    template<void(*F)(const T &)> 
    void f(T *= nullptr) { } 
}; 

また、あなたのコードで作業を打ち鳴らすことができます。

編集:

template <typename T, typename... U> 
struct S<T, U...>: S<T>, S<U...> { 
}; 

template<typename T> 
struct S<T> { 
    template<void(*F)(const T &)> 
    void f() { } 
} 

そして使用gであなたのfメソッドを呼び出します:

S<U>::template f<F>(); 

使用を

あなたが専門分野を変えることができるコードがさらに簡単にするためにこのオプションはさらに進んでいくことができます。@ barryはタグのディスパッチを使用することを提案しましたが、私はn個のテンプレートパラメータではなく、機能のPARAMTERとして:

template <typename... T> 
struct S; 

template <typename T> 
struct footag { }; 

template <typename T, typename... U> 
struct S<T, U...>: S<footag<T>>, S<U...> { 
}; 

template<typename T> 
struct S<T>:S<footag<T>> { 
}; 

template<typename T> 
struct S<footag<T>> { 
    template <void (*F)(const T&)> 
    void f() { } 
}; 

template<typename V, typename... T> 
struct R: S<V, T...> { 
    template<typename U, void(*F)(const U &)> 
    void g() { 
     S<footag<U>>::template f<F>(); 
    } 
}; 

void h(const float &) { } 

int main() { 
    R<int, float, double> r; 
    r.g<float, h>(); 
} 
+1

「NULL」ではなく「nullptr」がもっと* C++ 11 * -ishでしょうか? – skypjack

+0

もちろんそれは... :)編集をする –

+0

私はタグディスパッチングに頼ると思います。あなたが提案しているものは、ミックスインベースのアプローチのように見えますが、私が探しているものではありません(誤解しないでください、ミックスインが本当に好きですが、それは理にかなっていますか? – skypjack

関連する問題