使用 SFINAE 派生 类 的通用接口
Common interface for derived classes using SFINAE
我想向 C++ class 添加(动态)属性,它可以是多种类型(例如 float
、int
、bool
)。根据它们的值类型,界面中应该显示不同的控件。
为此,我使用 SFINAE 为 type()
函数创建了一个简单的 属性 class:
#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;
}
到目前为止,这工作得相当好。现在,当我想将这些属性中的几个存储在一个向量中时,问题就来了。为此,我创建了一个通用接口,并相应地更改了 class:
#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
class 定义。我尝试定义一个纯虚拟 IProperty::type()
,但这当然不适用于模板派生的 class.
我有哪些选择?
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
方法
专业化:
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;}
扩展当前解决方案的一个简单方法确实是在基础 class 中添加一个纯虚函数。为了使其编译,在派生的 class 中添加一个额外的虚函数,它甚至可以与模板化变体具有相同的名称(尽管它可能会造成混淆):
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;
您实际上可能希望将内部(模板化)type() 函数声明为私有,因为它们不是接口 (IPorperty) 的一部分,不应公开。这将禁止调用 fProp->type<>()
.
我想向 C++ class 添加(动态)属性,它可以是多种类型(例如 float
、int
、bool
)。根据它们的值类型,界面中应该显示不同的控件。
为此,我使用 SFINAE 为 type()
函数创建了一个简单的 属性 class:
#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;
}
到目前为止,这工作得相当好。现在,当我想将这些属性中的几个存储在一个向量中时,问题就来了。为此,我创建了一个通用接口,并相应地更改了 class:
#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
class 定义。我尝试定义一个纯虚拟 IProperty::type()
,但这当然不适用于模板派生的 class.
我有哪些选择?
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
方法
专业化:
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;}
扩展当前解决方案的一个简单方法确实是在基础 class 中添加一个纯虚函数。为了使其编译,在派生的 class 中添加一个额外的虚函数,它甚至可以与模板化变体具有相同的名称(尽管它可能会造成混淆):
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;
您实际上可能希望将内部(模板化)type() 函数声明为私有,因为它们不是接口 (IPorperty) 的一部分,不应公开。这将禁止调用 fProp->type<>()
.