使用SFINAE select接口来实现
Using SFINAE to select the interface to implement
我正在尝试 select 具体 class 基于模板参数实现的接口。在下面的简化示例中,有两个接口具有不同名称的方法 - Implementation
class 需要提供方法实现,具体取决于模板参数。
如果我使用 IntType
- 它应该实现 IntInterface
(getInt()
和 setInt(int)
)。
如果我使用 DoubleType
- 它应该实现 DoubleInterface
(getDouble()
和 setDouble(double)
)。
为了实现这一点,我创建了一个特征class,它决定了应该使用哪个接口(InterfaceType
),但也有另一个参数用于SFINAE。
我的想法是,如果我使用例如MyTypeTrait<Type>::MyTypeInt
但相关特征 class 没有 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
对于您是想支持 either/or 接口还是这 2 个接口的 complete/NOP 版本的问题,我不是很确定。
这是前者的一种解决方案。
#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();
}
这个:
// 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 仅适用于模板实例化的直接上下文中的替换失败。这些成员函数不在 class 模板实例化的直接上下文中,因此 MyTypeTrait<DoubleType>::MyTypeInt
是一个 硬错误 ,而不是替换失败。
您将不得不重新考虑您的设计。可能,您只有两个完全独立的接口实现,然后编写如下内容:
template <int Type>
using Implementation = std::conditional_t<
Type == IntType,
IntImplementation,
DoubleImplementation>;
或者只是对所有这些进行明确的模板专业化。
我正在尝试 select 具体 class 基于模板参数实现的接口。在下面的简化示例中,有两个接口具有不同名称的方法 - Implementation
class 需要提供方法实现,具体取决于模板参数。
如果我使用 IntType
- 它应该实现 IntInterface
(getInt()
和 setInt(int)
)。
如果我使用 DoubleType
- 它应该实现 DoubleInterface
(getDouble()
和 setDouble(double)
)。
为了实现这一点,我创建了一个特征class,它决定了应该使用哪个接口(InterfaceType
),但也有另一个参数用于SFINAE。
我的想法是,如果我使用例如MyTypeTrait<Type>::MyTypeInt
但相关特征 class 没有 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
对于您是想支持 either/or 接口还是这 2 个接口的 complete/NOP 版本的问题,我不是很确定。
这是前者的一种解决方案。
#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();
}
这个:
// 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 仅适用于模板实例化的直接上下文中的替换失败。这些成员函数不在 class 模板实例化的直接上下文中,因此 MyTypeTrait<DoubleType>::MyTypeInt
是一个 硬错误 ,而不是替换失败。
您将不得不重新考虑您的设计。可能,您只有两个完全独立的接口实现,然后编写如下内容:
template <int Type>
using Implementation = std::conditional_t<
Type == IntType,
IntImplementation,
DoubleImplementation>;
或者只是对所有这些进行明确的模板专业化。