使用SFINAE的派生类的通用接口

时间:2016-02-11 08:33:10

标签: c++ c++11 sfinae

我想将(动态)属性添加到C ++类中,该类可以是多种类型(例如floatintbool)。根据其值类型,应在界面中显示不同的控件。

为此,我使用SFINAE为type()函数创建了一个简单的Property类:

#include <iostream>
#include <type_traits>

template <class T>
class Property
{
public:
  enum Type {
    Undefined = -1,
    Int,
    Float,
    Bool,
  };

  explicit Property(const std::string& name) : name_(name) { }

  const std::string& name() const { return name_; }

  // specialization for floating point type() getter
  template<class U = T,
           typename std::enable_if<std::is_floating_point<U>::value>::type* = nullptr>
  Type type() const {
    return Type::Float;
  }

  // specialization for integer type() getter
  template<class U = T,
           typename std::enable_if<std::is_integral<U>::value>::type* = nullptr>
  Type type() const {
    return Type::Int;
  }

  // specialization for boolean type() getter
  template<class U = T,
           typename std::enable_if<std::is_same<U, bool>::value>::type* = nullptr>
  Type type() const {
    return Type::Bool;
  }

private:
  std::string name_;
  T value_;
};

int main() {
  // this works
  auto fProp = new Property<float>("float property");
  std::cout << fProp->type() << std::endl;
}

到目前为止,这种方法运作良好。现在,当我想在向量中存储其中的几个属性时,问题就出现了。为此,我创建了一个通用接口,并相应地更改了类:

#include <iostream>
#include <type_traits>
#include <vector>

class IProperty
{
  // common interface for all typed Property<T>'s
public:
  enum Type {
    Undefined = -1,
    Int,
    Float,
    Bool,
  };

  virtual const std::string& name() const = 0;
};

template <class T>
class Property : public IProperty
{
public:
  explicit Property(const std::string& name) : name_(name) { }

  const std::string& name() const { return name_; }

  // specialization for floating point type() getter
  template<class U = T,
           typename std::enable_if<std::is_floating_point<U>::value>::type* = nullptr>
  Type type() const {
    return Type::Float;
  }

  // specialization for integer type() getter
  template<class U = T,
           typename std::enable_if<std::is_integral<U>::value>::type* = nullptr>
  Type type() const {
    return Type::Int;
  }

  // specialization for boolean type() getter
  template<class U = T,
           typename std::enable_if<std::is_same<U, bool>::value>::type* = nullptr>
  Type type() const {
    return Type::Bool;
  }

private:
  std::string name_;
  T value_;
};

int main() {

  // works
  auto fProp = new Property<float>("float property");
  std::cout << fProp->type() << std::endl;

  std::vector<IProperty*> properties;
  properties.push_back(fProp);

  // error: 'class IProperty' has no member named 'type'
  for (auto iprop : properties) {
    std::cout << iprop->type() << std::endl;  
  }

}

如您所见,我无法调用type()方法,因为它没有为IProperty类定义。我尝试定义纯虚拟IProperty::type(),但当然这不适用于模板化的派生类。

我有什么选择?

3 个答案:

答案 0 :(得分:2)

专业化:

class IProperty
{
  // common interface for all typed Property<T>'s
public:
  enum Type {
    Undefined = -1,
    Int,
    Float,
    Bool,
  };

  virtual const std::string& name() const = 0;
  virtual Type type() const { return Type::Undefined }
};

template <class T>
class Property : public IProperty
{
public:
  explicit Property(const std::string& name) : name_(name) { }

  const std::string& name() const override { return name_; }

  Type type() const override;

private:
  std::string name_;
  T value_;
};

template <> Type Property<float>::type() const { return Type::Float;}
template <> Type Property<int>::type() const { return Type::Int;}
template <> Type Property<bool>::type() const { return Type::Bool;}

答案 1 :(得分:1)

class IProperty 
{ 
public:
  virtual Type getType() const=0; 
  enum {...}
};

template < class T > class PropertyByType : public IProperty
{
  // implement here the differents type() method 

  // then :
  virtual Type getType(){ return type<T>();}
}

template < class T >
class Property : public PropertyByType<T>
{
  // ...
}

现在Property<T>*可投放到IProperty*,因此可以访问getType方法

答案 2 :(得分:1)

扩展当前解决方案的一种简单方法是在基类中添加纯虚函数。要使其编译,请在派生类中添加一个额外的虚函数,该函数甚至可以与模板化变体具有相同的名称(但它可能会令人困惑):

class IProperty
{
    // ...
    virtual Type type() const = 0;
};

template <class T>
class Property : public IProperty
{
public:
    // ...

    // here type() is not a template, so it can be virtual
    virtual Type type() const override
    {
        return type<T>();
    }
};

现在,如果您使用类似

的内容
auto fProp = new Property<float>("float property");
std::cout << fProp->type() << std::endl;

它实际上会调用新的接口函数而不是模板化的函数。

要直接调用模板,您必须明确说出:

auto fProp = new Property<float>("float property");
// note the <>
std::cout << fProp->type<>() << std::endl;

您实际上可能希望将内部(模板化)类型()函数声明为私有,因为它们不是接口(IPorperty)的一部分,因此不应公开。这将禁止调用fProp->type<>()