2014-01-20 12 views
6

テンプレートメンバ関数は、使用される場合にのみ生成されることを理解しています。これは、すべての使用された型がそのような関数をサポートしていない場合に便利です。しかし、これは後続の戻り型指定を持つ関数では機能していないようです。以下は、小さな実験です:未使用の場合でもエラーを返す末尾の戻り値型を持つテンプレートメンバ関数

// helper function for case A workaround 
template <typename A, typename T> 
auto F(T&& x) 
-> decltype(x.template f <A>()) 
    { return x.template f <A>(); } 

// helper function for case B workaround 
template <typename A, typename T> 
auto G(T&& x) 
-> decltype(x.g()) 
    { return x.g(); } 

template <typename T> 
struct S 
{ 
    // case A: not ok in GCC + Clang 
    template <typename A> 
    auto f1() 
    -> decltype(T().template f <A>()) 
     { return T().template f <A>(); } 

    // case A workaround: ok in Clang + GCC 
    template <typename A> 
    auto f2() 
    -> decltype(F <A>(T())) 
     { return F <A>(T()); } 

    // case B: ok in GCC, not ok in Clang 
    template <typename A> 
    auto g1() 
    -> decltype(T().g()) 
     { return T().g(); } 

    // case B workaround: ok in GCC + Clang 
    template <typename A> 
    auto g2() 
    -> decltype(G <A>(T())) 
     { return G <A>(T()); } 
}; 

このサンプルは、唯一の問題を説明することを意味し、それは何か他のものに有用ではないことを心に留めておいてください。

S <T>は、適切なメンバー関数f,gを持つ任意のタイプTでインスタンス化できます。

ただし、S <int>をインスタンス化しようとすると、 S <int> s{};によって、type 'int' is not a structure or unionのようなエラーが表示されます。これは、タイプT(この場合はint)の値に対してそれぞれf1,g1のテンプレート関数fまたはテンプレート以外の関数gを呼び出す場合に発生します。オブジェクトsf1またはg1を呼び出そうとしていないにもかかわらず、それが起こります。しかし、GCCはg1の場合は問題ありません。 Clangはそうではありません。

ケースA(テンプレートメンバー関数f)の回避策は、Fというヘルパー関数を使用することです。これはf2であり、ClangとGCCの両方でうまく機能します。呼び出しT().template f <A>()f2の宣言から隠されており、Aが不明の場合、コンパイラはF <A>(T())を探していないため、動作するようです。

ケースB(非テンプレートメンバー関数g)の同じ回避策も両方のコンパイラで機能しています。

私は何が起こっているのか調べるのに助けていただきたいと思います。いずれの場合も正しい行動はどれですか?どのコンパイラが正しいのですか?一般的に回避策がありますか?

私はGCC 4.8.1とClang 3.3を使用しています。

+1

@Gasim醜いかもしれないが、残念ながら 'template'キーワードが必要です。例えば、 'f'はデータメンバーかもしれないので、' x.f iavr

+0

['std :: enable_if'](http://en.cppreference.com/w/cpp/types/enable_if)について知っていますか? – Constructor

答えて

1

SFINAEは、クラスの継承した関数ではなく、関数のテンプレート引数にのみ適用されます。

別の解決策は、第2のテンプレート引数にコピーTを含めることですが、これはあなたの回避策の短いバージョン以外の何ものでもありません:

#include <utility> 
#include <type_traits>  
struct Foo { 
    template < typename T > T f() { return {}; } 
}; 
template <typename T> 
struct S { 
    template <typename A, typename TT = T > 
    auto f1() -> decltype(std::declval<TT>().template f <A>()) { 
     static_assert(std::is_same<T,TT>::value, "TT must be equal to T"); 
     return TT().template f <A>(); 
    } 
}; 

int main() { 
    S<Foo> a; 
    a.f1<int>(); // ok 

    S<int> b; 
    b.f1<int>(); // not ok 
} 
+0

ありがとう、この回避策は間違いなく良いことですが、私はそれを念頭に置いておきます(現時点では、コードをより一般的なものにする追加機能を使用して問題を回避しました)。私は、テンプレート以外のデフォルトのコンストラクタをenable_if(デフォルトのコンストラクタには引数も返さない型もないので、テンプレートにならない限り、どこにも置くことができません)を制御するためにダミーのテンプレート引数を導入するという考え方を使用しました。 – iavr

+0

@iavr上記の結果は、診断を必要としないプログラムが作成されます:すべての 'template'関数は少なくとも1つの特殊化を有効にする必要があります。 – Yakk

関連する問題