策略库设计:有条件地更改策略 class 中的成员变量

policy base design: conditionally change a member variable from policy class

我的程序定义了一个 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 配置为 class 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

使用 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

嗯...简单地修改 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 来 activate/disactivate 两个不同的构造函数。

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