1

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

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

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

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

能够从标题中获取信息的东西(还不知道是什么)在哪里 - 判断expr应该是真还是假。理想情况下,如果我创建一个新的派生类但未能在标头中指定标志,我会收到错误消息,但这不是强制性的。这样我可以只修改一个中心位置来进行更改。Derived1flag

什么是惯用的方法?

4

4 回答 4

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;
}

theFlag这基本上依赖于 SFINAE -如果您使用不受支持的参数调用它,编译器将无法找到重载,本质上。

于 2013-02-04T12:19:16.803 回答
2

你可以写:

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

您的标题可能具有以下内容:

template <typename T>
struct Concept
{};

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

为每种类型重复专业化。

这是你的意思吗?当您没有为 some 提供标志值时,编译将失败DerivedN

于 2013-02-04T12:15:25.273 回答
2

我会为标志编写一个特征类并使用宏来定义特化:

#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) {}
};
于 2013-02-04T13:49:51.583 回答
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 可能会导致未定义的行为 (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 ;
}
于 2013-02-04T12:29:14.787 回答