【问题标题】:policy base design: conditionally change a member variable from policy class策略库设计:有条件地更改策略类的成员变量
【发布时间】:2021-06-15 13:15:49
【问题描述】:

我的程序定义了一个Animal 结构,可以使用CanSwim/CanNotSwimCanBark/CanNotBark 进行配置:

#include <iostream>

struct CanSwim {
};

struct CanNotSwim {
};

struct CanBark {
    CanBark() : volume(10) {}
    void bark() {
        std::cout << "bark at volume " << volume << std::endl;
    }

    void setVolume(int newVolume) {
        volume = newVolume;
    }

private:
    int volume;
};

struct CanNotBark {
};

template<class SwimType, class BarkType>
struct Animal : public SwimType, public BarkType {
    Animal() = default;
};

int main() {
    auto dog = Animal<CanSwim, CanBark>();
    dog.bark();

    auto cat = Animal<CanNotSwim, CanBark>();
    cat.bark();

    return 0;
}

现在,当Animal 配置了类CanNotSwim(在编译时)时,如何设置规则以自动将CanBark::volume 加倍?拥有:

    auto dog = Animal<CanSwim, CanBark>();
    dog.bark();
    // "bark at volume 10"

    auto cat = Animal<CanNotSwim, CanBark>();
    cat.bark();
    // "bark at volume 20"

我不打算将函数 bark()CanBark 移动到 Animal

【问题讨论】:

  • 基于策略的设计的一个主要特点是策略是正交的。如果您引入这种依赖关系,则正交性不成立。如果发生这种情况,请考虑更深入地考虑您的政策。
  • @StoryTeller-UnslanderMonica 对于我的设计,大多数策略是正交的,其中一些是相关的。

标签: c++ templates template-meta-programming


【解决方案1】:

使用 CRTP,您可能会获得该信息:

struct CanSwim {};
struct CanNotSwim {};

template <typename Der>
struct CanBark {
    CanBark() : volume(std::is_base_of_v<CanNotSwim, Der> ? 20 : 10) {}
    void bark() {
        std::cout << "bark at volume " << volume << std::endl;
    }

    void setVolume(int newVolume) { volume = newVolume; }

private:
    int volume;
};

template <typename Der>
struct CanNotBark {};

template<class SwimType, template <typename> class BarkType>
struct Animal : public SwimType, public BarkType<Animal<SwimType, BarkType>> {
    Animal() = default;
};

int main() {
    auto dog = Animal<CanSwim, CanBark>();
    dog.bark();

    auto cat = Animal<CanNotSwim, CanBark>();
    cat.bark();
}

Demo

您也可以在构造函数中提供信息:

struct CanSwim {};
struct CanNotSwim {};

struct CanBark {
    CanBark(bool canSwim) : volume(canSwim ? 10 : 20) {}
    void bark() {
        std::cout << "bark at volume " << volume << std::endl;
    }

    void setVolume(int newVolume) { volume = newVolume; }

private:
    int volume;
};


struct CanNotBark {
    CanNotBark(bool canSwim) {}
};

template<class SwimType, class BarkType>
struct Animal : public SwimType, public BarkType {
    Animal() : BarkType(std::is_same_v<SwimType, CanSwim>) {}
};

int main() {
    auto dog = Animal<CanSwim, CanBark>();
    dog.bark();

    auto cat = Animal<CanNotSwim, CanBark>();
    cat.bark();
}

Demo

【讨论】:

  • 用原来的继承结构可以吗? struct Animal : public SwimType, public BarkType&lt;Animal&lt;SwimType, BarkType&gt;&gt; 在我看来有点纠结。
  • 有一个提议deducing this 允许更简单的语法。
  • @Rahn 这个解决方案是必要的。也许您应该将所有策略都设为 CRTP 模板。
  • 添加了另一种选择。
【解决方案2】:

嗯...简单地修改Animal 构造函数以使用正确的值调用setVolume()(如果BarkTypeCanBark)怎么样?

我的意思是……像

template<class SwimType, class BarkType>
struct Animal : public SwimType, public BarkType {
    Animal ()
    {
       if constexpr ( std::is_same_v<BarkType, CanBark> )
          BarkType::setVolume(std::is_same_v<SwimType, CanNotSwim> ? 20 : 10);
    }
};

这显然需要至少 C++17(if constexpr 是在 C++17 中引入的)或 setVolume() 调用在 CanNotBark 的情况下给出和错误。在 C++17 之前,您必须使用 SFINAE 来激活/停用两个不同的构造函数。

否则,如果您在构建时需要直接在CanBark 中获得正确的音量值,则必须将信息作为模板参数传递,如 Jarod42 的答案所示。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-03
    • 2021-03-29
    • 2014-05-26
    • 2014-11-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-30
    相关资源
    最近更新 更多