C ++中策略设计模式的模板和函数指针之间有什么区别

What are the differences between a template and a function pointer for a Strategy design pattern in C++

我正在用 C++ 实现一个策略设计模式,我发现了几个我可以采用的不同选项。一种方法是使用模板,可能如下所示:

namespace Strategies
{
    struct IsEven
    {
        bool doSomething(int x) { return x % 2 == 0; }
    };

    struct IsOdd
    {
        bool doSomething(int y) { return y % 2 == 1; }
    };
}


template<typename Strategy>
class Processor
{
public:
    Processor() : _strat{ Strategy() }
    {}

    bool process(int num)
    {
        return _strat.doSomething(num);
    }

private:
    Strategy _strat;
};

int main()
{
    Processor<Strategies::IsEven> templateOne{};
    Processor<Strategies::IsOdd> templateTwo{};
    std::cout << "Process 4 with One: " << (templateOne.process(4) ? "True" : "False") << std::endl;
    std::cout << "Process 4 with Two: " << (templateTwo.process(4) ? "True" : "False") << std::endl;
}

另一种方法是使用函数指针,可能如下所示:

#include <functional>

class ProcessorTwo
{
public:
    using StrategyFunction = std::function<bool(int)>;
    static bool lessThanFive(int num) {
        return num < 5;
    }

    static bool greaterThanFive(int num) {
        return num > 5;
    }

    ProcessorTwo(StrategyFunction strat) : _strat{ strat }
    {}

    bool process(int num) {
        return _strat(num);
    }
    
private:
    StrategyFunction _strat;
};

int main()
{
    ProcessorTwo functionPointerOne{ ProcessorTwo::greaterThanFive };
    ProcessorTwo functionPointerTwo{ ProcessorTwo::lessThanFive };
    std::cout << "Process 4 with One: " << (functionPointerOne.process(4) ? "True" : "False") << std::endl;
    std::cout << "Process 4 with Two: " << (functionPointerTwo.process(4) ? "True" : "False") << std::endl;
}

我会知道编译时会用到的函数。我对 C++ 还很陌生,所以我不太了解这两种方法之间的区别。我听说函数指针添加了一个间接层,但是保存函数的结构不也是如此吗?是否有更“惯用”的 c++ 选项,或者它只是取决于开发人员的偏好?

主要区别在于模板版本使用“编译时多态性”,这意味着策略的选择发生在编译时并且策略是类型的一部分。因此,Processor<T> 的特定实例使用的策略必须在编译时已知,并且在创建 Processor<T> 后不能动态更改。

但是,您可以添加一个成员函数,以便 ProcessorTwo 使用的策略可以动态更改。例如,您还可以编写一个函数,该函数将 std::function<bool(int)> 作为参数并在函数体中创建一个 ProcessorTwo ;您不能使用 Processor<T> 执行此操作,除非它是函数模板。

因此,如果您需要那种动态性,函数指针或基于 std::function 的方法是可行的方法,但如果您不需要它,那么模板版本会更有效,因为调用策略不会涉及运行时间接。