派生类构造函数中的数据驱动标志设置

时间:2013-02-04 12:10:11

标签: c++ design-patterns data-driven

假设我有一个基类,其中包含一个派生类必须设置的标志:

struct Base
{
    bool flag;
    Base(bool flag):flag(flag) {}
};

我想配置哪些派生类以数据驱动的方式将标志设置为true / false - 即我想从标头配置它。

struct Derived1 : Base
{
    Derived1() : Base( expr ) {}
};

expr 某些(不知道还有什么)能够从标题中获取信息的地方 - 告诉我Derived1是否应该flag } 对或错。理想情况下,如果我创建一个新的派生类但是未能在标头中指定标志,我会收到错误,但这不是强制性的。这样我就可以修改单个中心位置进行更改。

这方面的惯用方法是什么?

4 个答案:

答案 0 :(得分:2)

你可以写:

struct Derived1 : Base
{
    Derived1() : Base(Concept<Derived1>::theFlag) {}
};

您的标题可能包含以下内容:

template <typename T>
struct Concept
{};

template <>
struct Concept<Derived1>
{
   static const bool theFlag = true;
};

对每种类型重复专业化。

这是你的意思吗?如果没有为某些DerivedN提供标记值,则编译将失败。

答案 1 :(得分: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找到重载。

答案 2 :(得分:2)

我会为flags编写一个trait类,并使用宏来定义特化:

#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) {}
};

答案 3 :(得分:1)

这是一个纯编译时解决方案:

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会导致Undefined 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 ;
}