提问人:Luchian Grigore 提问时间:2/4/2013 更新时间:2/4/2013 访问量:625
派生类构造函数中的数据驱动标志设置
Data-driven flag setting in derived class constructor
问:
假设我有一个基类,里面有一个派生类必须设置的标志:
struct Base
{
bool flag;
Base(bool flag):flag(flag) {}
};
我想配置哪些派生类以数据驱动的方式将标志设置为 / - 即我想从标头配置它。true
false
struct Derived1 : Base
{
Derived1() : Base( expr ) {}
};
哪里有东西(还不知道是什么)能够从标题中获取信息 - 判断是否应该为真或假。理想情况下,如果我创建一个新的派生类但未能在标头中指定标志,我会收到错误,但这不是强制性的。这样,我只需修改单个中心位置即可进行更改。expr
Derived1
flag
对此的惯用方法是什么?
答:
2赞
Lightness Races in Orbit
2/4/2013
#1
你可以这样写:
struct Derived1 : Base
{
Derived1() : Base(Concept<Derived1>::theFlag) {}
};
您的标头可以包含以下内容:
template <typename T>
struct Concept
{};
template <>
struct Concept<Derived1>
{
static const bool theFlag = true;
};
对每种类型重复专业化。
你是这个意思吗?当您没有为某些 .DerivedN
评论
0赞
Gearoid Murphy
2/4/2013
也许它是一个运行时参数?
0赞
Lightness Races in Orbit
2/4/2013
@Gearoid:在运行时无法决定标头的内容。
0赞
Luchian Grigore
2/4/2013
@GearoidMurphy没有。现在是编译时。@轻盈是的,与此类似,但我想要一个更紧凑的版本。
0赞
Lightness Races in Orbit
2/4/2013
@LuchianGrigore:我不确定我能不能打败它。C++ 没有任何类型的本机数据交换格式。
0赞
Gearoid Murphy
2/4/2013
枚举的常量可以达到同样的效果吗?
2赞
Lightness Races in Orbit
2/4/2013
#2
使用单个函数的替代版本可能更紧凑:
struct Derived1 : Base
{
Derived1() : Base(theFlag(this)) {}
};
然后在标题中:
template <typename T>
bool theFlag(T*)
{
if (typeid(T) == typeid(Derived1)) return true;
if (typeid(T) == typeid(Derived2)) return false;
if (typeid(T) == typeid(Derived3)) return true;
throw std::runtime_error("No theFlag is given for this type");
}
如果你已经习惯了编译时检查,你能做的最好的事情就是引入一些重复:
template <typename T>
bool theFlag(T*)
{
static_assert(
std::is_same<T, Derived1>::value ||
std::is_same<T, Derived2>::value ||
std::is_same<T, Derived3>::value,
"No theFlag is given for this type"
);
if (typeid(T) == typeid(Derived1)) return true;
if (typeid(T) == typeid(Derived2)) return false;
if (typeid(T) == typeid(Derived3)) return true;
}
这基本上依赖于 SFINAE - 如果您使用不受支持的参数调用它,编译器将无法找到重载。theFlag
评论
0赞
Luchian Grigore
2/4/2013
嗯,这个看起来更好,是的。
0赞
Lightness Races in Orbit
2/4/2013
我不相信这是对的。也就是说,我敢肯定不是。也许有人可以在我最初的 5 分钟结束之前帮助我纠正它?:Pstatic_assert
0赞
Puppy
2/4/2013
这是完全错误的。要么在编译时通过模板专用化执行类型调度,在这种情况下可以static_assert,要么断言将始终失败。
0赞
Lightness Races in Orbit
2/4/2013
@DeadMG:是的,我不认为我能进一步改进这个问题,除非最终回到另一个答案。
1赞
Gearoid Murphy
2/4/2013
#3
下面是一个纯编译时解决方案:
struct Derived1 ;
struct Derived2 ;
template <typename Derived> struct Bootstrap
{
bool init(Derived1 *) { return true ; }
bool init(Derived2 *) { return false ; }
Bootstrap():flag(init(static_cast<Derived *>(this))){}
bool flag ;
};
struct Derived1: public Bootstrap <Derived1> {};
struct Derived2: public Bootstrap <Derived2> {};
int main()
{
Derived1 d1 ;
Derived2 d2 ;
std::cout<<d1.flag<<" "<<d2.flag<<std::endl ;
return 0 ;
}
编辑
正如“轨道上的轻度竞赛”所指出的那样,在ctor过程中static_cast会导致不明确的Behabiour(UB)。下面是一个更新的实现,它否定了对 static_cast 运算符的需求:
#include <iostream>
struct Derived1 ;
struct Derived2 ;
namespace BootstrapDetail
{
template <typename Identifier> bool init();
template <> bool init <Derived1>() { return true ; }
template <> bool init <Derived2>() { return false ; }
}
template <typename Derived> struct Bootstrap
{
Bootstrap(): flag(BootstrapDetail::init<Derived>()) {}
bool flag ;
};
struct Derived1: public Bootstrap <Derived1> {};
struct Derived2: public Bootstrap <Derived2> {};
int main()
{
Derived1 d1 ;
Derived2 d2 ;
std::cout<<d1.flag<<" "<<d2.flag<<std::endl ;
return 0 ;
}
评论
0赞
Xeo
2/4/2013
struct Derived1*
和咳嗽struct Derived2*
0赞
Gearoid Murphy
2/4/2013
在 C++ 中是否需要遵循该约定?
3赞
Xeo
2/4/2013
不,但它使代码更短。:)它实际上是一个内联声明,这意味着您不再需要顶部的 and。struct Derived1;
struct Derived2;
0赞
Gearoid Murphy
2/4/2013
太棒了,我不知道。它也适用于“类”结构吗?
2赞
Lightness Races in Orbit
2/4/2013
@GearoidMurphy:不,在初始化期间,某些静态强制转换也会受到限制(例如,参见 12.7/3)。虽然我找不到任何禁止你的代码的措辞。
2赞
jrok
2/4/2013
#4
我会为标志编写一个特征类,并使用宏来定义专业化:
#include <type_traits>
template<typename T>
struct FlagTrait {
static_assert(std::is_void<T>::value, "No flag defined for this type.");
};
#define DEFINE_FLAG(Type, Val) \
template<> \
struct FlagTrait<class Type> \
: std::integral_constant<bool, Val> {};
template<typename T>
constexpr bool getFlag(T) { return FlagTrait<T>::value; }
#define GET_FLAG getFlag(*this)
现在,对于每个新的派生类,我们需要做的就是添加一行带有类名和标志值的行:
DEFINE_FLAG(Derived1, true)
DEFINE_FLAG(Derived2, false)
用法:
struct Base
{
bool flag;
Base(bool flag):flag(flag) {}
};
struct Derived1 : Base
{
Derived1() : Base(GET_FLAG) {}
};
struct Derived2 : Base
{
Derived2() : Base(GET_FLAG) {}
};
下一个:如何在MSVS中查看设置标志?
评论
constexpr
declval<T>()