2016-08-23 22 views
2

具体的なクラスがテンプレートパラメータに基づいて実装するインターフェイスを選択しようとしています。以下の簡単な例では、異なる名前のメソッドを持つ2つのインタフェースがあります。Implementationクラスは、テンプレートのパラメータに応じてメソッドの実装を提供する必要があります。SFINAEを使用して実装するインターフェイスを選択する

IntTypeを使用する場合は、IntInterfacegetInt()setInt(int))を実装する必要があります。 DoubleTypeを使用する場合は、DoubleInterfacegetDouble()setDouble(double))を実装する必要があります。

これを達成するために、使用するインターフェイス(InterfaceType)を決定するtraitsクラスを作成しましたが、SFINAEに使用する別のパラメータもあります。

考えてみましょう。 MyTypeTrait<Type>::MyTypeIntであるが、関連形質クラスはMyTypeIntの定義を持たず、コンパイラはこの可能性のあるオーバーロード(例えばsetInt()など)をスローし、別のものを使用する。それはダミーが蹴られるべき場所です - 彼らは異なった議論を持ち、彼らはバーチャルではありません。

ただし、動作しません。下記のコンパイラエラーを参照してください。

私はVisual Studio 2013(VC12)を使用しています。

struct IntInterface 
{ 
    virtual int getInt() const = 0; 
    virtual void setInt(int value) = 0; 
}; 

struct DoubleInterface 
{ 
    virtual double getDouble() const = 0; 
    virtual void setDouble(double value) = 0; 
}; 

const int IntType = 0; 
const int DoubleType = 1; 

template <int Type> 
struct MyTypeTrait; 

template <> 
struct MyTypeTrait<IntType> 
{ 
    using MyTypeInt = int; 
    using InterfaceType = IntInterface; 
}; 

template <> 
struct MyTypeTrait<DoubleType> 
{ 
    using MyTypeDouble = double; 
    using InterfaceType = DoubleInterface; 
}; 

template <int Type> 
struct Implementation : public MyTypeTrait<Type>::InterfaceType 
{ 
    // Actual interface implementation for the case of IntType 
    virtual typename MyTypeTrait<Type>::MyTypeInt getInt() const override { return 0; } 
    virtual void setInt(typename MyTypeTrait<Type>::MyTypeInt value) override {} 

    // Dummys for SFINAE - to be used in the case of DoubleType 
    typename int getInt(int) const { return 0; } 
    void setInt() {} 

    // Actual interface implementation for the case of DoubleType 
    virtual typename MyTypeTrait<Type>::MyTypeDouble getDouble() const override { return 0.0; } 
    virtual void setDouble(typename MyTypeTrait<Type>::MyTypeDouble value) override {} 

    // Dummys for SFINAE - to be used in the case of IntType 
    typename double getDouble(int) const { return 0.0; } 
    void setDouble() {} 
}; 


int main(int argc, char* argv[]) 
{ 
    Implementation<IntType> myInt; 
    Implementation<DoubleType> myDouble; 
} 

コンパイラエラー:

1>c++-tests.cpp(50): error C2039: 'MyTypeDouble' : is not a member of 'MyTypeTrait<0>' 
1>   c++-tests.cpp(26) : see declaration of 'MyTypeTrait<0>' 
1>   c++-tests.cpp(61) : see reference to class template instantiation 'Implementation<0>' being compiled 
1>c++-tests.cpp(50): error C2146: syntax error : missing ';' before identifier 'getDouble' 
1>c++-tests.cpp(50): error C2433: 'Implementation<0>::MyTypeDouble' : 'virtual' not permitted on data declarations 
1>c++-tests.cpp(50): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int 
1>c++-tests.cpp(50): warning C4183: 'getDouble': missing return type; assumed to be a member function returning 'int' 
1>c++-tests.cpp(51): error C2039: 'MyTypeDouble' : is not a member of 'MyTypeTrait<0>' 
1>   c++-tests.cpp(26) : see declaration of 'MyTypeTrait<0>' 
1>c++-tests.cpp(51): error C2061: syntax error : identifier 'MyTypeDouble' 
1>c++-tests.cpp(55): error C2535: 'void Implementation<0>::setDouble(void)' : member function already defined or declared 
1>   c++-tests.cpp(51) : see declaration of 'Implementation<0>::setDouble' 
1>c++-tests.cpp(50): error C3668: 'Implementation<0>::getDouble' : method with override specifier 'override' did not override any base class methods 
1>c++-tests.cpp(51): error C3668: 'Implementation<0>::setDouble' : method with override specifier 'override' did not override any base class methods 
1>c++-tests.cpp(42): error C2039: 'MyTypeInt' : is not a member of 'MyTypeTrait<1>' 
1>   c++-tests.cpp(33) : see declaration of 'MyTypeTrait<1>' 
1>   c++-tests.cpp(62) : see reference to class template instantiation 'Implementation<1>' being compiled 
1>c++-tests.cpp(42): error C2146: syntax error : missing ';' before identifier 'getInt' 
1>c++-tests.cpp(42): error C2433: 'Implementation<1>::MyTypeInt' : 'virtual' not permitted on data declarations 
1>c++-tests.cpp(42): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int 
1>c++-tests.cpp(42): warning C4183: 'getInt': missing return type; assumed to be a member function returning 'int' 
1>c++-tests.cpp(43): error C2039: 'MyTypeInt' : is not a member of 'MyTypeTrait<1>' 
1>   c++-tests.cpp(33) : see declaration of 'MyTypeTrait<1>' 
1>c++-tests.cpp(43): error C2061: syntax error : identifier 'MyTypeInt' 
1>c++-tests.cpp(47): error C2535: 'void Implementation<1>::setInt(void)' : member function already defined or declared 
1>   c++-tests.cpp(43) : see declaration of 'Implementation<1>::setInt' 
1>c++-tests.cpp(42): error C3668: 'Implementation<1>::getInt' : method with override specifier 'override' did not override any base class methods 
1>c++-tests.cpp(43): error C3668: 'Implementation<1>::setInt' : method with override specifier 'override' did not override any base class methods 
+0

このコンパイラは、C++ 11をサポートしていますか? –

+0

コンパイラが古すぎて、現在のC++標準をサポートできません。 –

+0

@RichardHodges MSVC2013はC++ 11のサポートを主張しています。それは不安定で、SFINAEはそれが最悪のものの1つです。 – jaggedSpire

答えて

1

私はあなたがどちらか/またはインタフェースまたは2つのインターフェイスの完全な/ NOPのバージョンをサポートしたいかどうかの問題から完全に確認されませんでした。

これは前者の1つの解決策です。

#include <utility> 
#include <type_traits> 

struct IntInterface 
{ 
    virtual int getInt() const = 0; 
    virtual void setInt(int value) = 0; 
}; 

template<class TrueFalse> 
struct IntInterfaceImpl {}; 

template<> 
struct IntInterfaceImpl<std::true_type> : IntInterface 
{ 
    int getInt() const override { return i_; } 
    void setInt(int value) override { i_ = value; } 

    int i_; 
}; 

struct DoubleInterface 
{ 
    virtual double getDouble() const = 0; 
    virtual void setDouble(double value) = 0; 
}; 

template<class TrueFalse> 
struct DoubleInterfaceImpl {}; 

template<> 
struct DoubleInterfaceImpl<std::true_type> : DoubleInterface 
{ 
    double getDouble() const override { return i_; } 
    void setDouble(double value) override { i_ = value; } 

    double i_; 
}; 


enum type { 
    is_int, 
    is_double 
}; 

template<type T> 
struct Implementation 
: IntInterfaceImpl<std::integral_constant<bool, T == is_int>> 
, DoubleInterfaceImpl<std::integral_constant<bool, T == is_double>> 
{ 

}; 


int main() 
{ 
    Implementation<type::is_int> i {}; 
    i.setInt(6); 
    int a = i.getInt(); 

    Implementation<type::is_double> d {}; 
    d.setDouble(6.0); 
    int b = d.getDouble(); 
} 
+0

私はあなたの答えに似たようなことをしたので、私はそれを受け入れます。私は各インターフェイスとクラスの間にアダプタを追加しました。おかげさまで – Asaf

1

この:

// Actual interface implementation for the case of IntType 
virtual typename MyTypeTrait<Type>::MyTypeInt getInt() const override { return 0; } 
virtual void setInt(typename MyTypeTrait<Type>::MyTypeInt value) override {} 

// Dummys for SFINAE - to be used in the case of DoubleType 
int getInt(int) const { return 0; } 
void setInt() {} 

は、作業のない希望を持っていません。 SFINAEは、テンプレートインスタンシエーションの直後のコンテキストでの置換失敗に対してのみ使用されます。これらのメンバ関数は、クラステンプレートインスタンシエーションの直後のコンテキストにはないため、MyTypeTrait<DoubleType>::MyTypeIntハードエラーであり、置換に失敗しません。

デザインを考え直す必要があります。おそらく、あなただけの2つの完全に独立したインターフェースの実装を持っているし、その後のようなものを書く:

template <int Type> 
using Implementation = std::conditional_t< 
    Type == IntType, 
    IntImplementation, 
    DoubleImplementation>; 

またはちょうど明白なこれらのすべてのテンプレートの特殊化を。

+0

お返事ありがとうございます。オーバーロードを選択するときではなく、 'Implement 'をインスタンス化するときに起こるため、ハードエラーと見なされることを正しく理解していますか? 私の実際の問題BTWはもう少し複雑です - 両方のインターフェイスが同じインターフェイスから継承し、差別化する呼び出しを別のクラス(テンプレートパラメータによっても決まります)に転送する必要があるため、あなたの提案は適合しませんそこ。 – Asaf

+0

@Asafそれは正しいです。 – Barry

関連する問題