c++ - 使用 SFINAE 选择要实现的接口(interface)

标签 c++ visual-c++ sfinae

我试图根据模板参数选择具体类实现的接口(interface)。在下面的简化示例中,有两个具有不同名称的方法的接口(interface) - Implementation类需要提供方法实现,具体取决于模板参数。

如果我使用IntType - 它应该实现IntInterface ( getInt()setInt(int) )。 如果我使用 DoubleType - 它应该实现DoubleInterface ( getDouble()setDouble(double) )。

为了实现这一目标,我创建了一个特征类,它确定应使用哪个接口(interface) ( InterfaceType ),但还有另一个用于 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

最佳答案

从这个问题中我不能完全确定您是否想要支持任一/或接口(interface)或这 2 个接口(interface)的完整/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();
}

关于c++ - 使用 SFINAE 选择要实现的接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39111474/

相关文章:

c++ - SFINAE 尝试使用 bool 给出编译器错误 : "template argument ‘T::value’ involves template parameter"

c++ pimpl 和抽象类在一起

c++ - 函数 v8::Value::IsInt32 没有地址

c++ - 将非常大且旧的 C++Builder 代码移植/重新编码到 Qt 或 CLI/Mono

c++ - 是否可以检查是否为类定义了成员函数,即使成员是从未知基类继承的

c++ - 使用 SFINAE 检测成员函数

c++ - 如何循环绘制事物(FLTK/C++)?

c++ - 用逗号分隔值比较两个字符串

c++ - 四元数的标准位置?

gcc - 如何在为 gcc 和 vc++ 编写通用代码时克服 vc++ 警告 C4003