在模板的帮助下,C++ 构造函数根据输入表现不同

C++ constructor behave differently according to input, with help of template

我正在尝试使用模板来初始化一个对象。代码应根据不同的参数输入分配 s,特别是“类型”。但是,我无法让它工作。

#include <iostream>
#include <cmath>

using namespace std;

// different discount

class Strategy {
public:
    virtual double GetResult(double) = 0;
};

class SellNormal: public Strategy {
public:
    double GetResult(double original) override {
        return original;
    }
};

class SellDiscount: public Strategy {
    double rate;
public:
    SellDiscount(double r){
        rate = r;
    }
    double GetResult(double original) override {
        return original * rate;
    }
};
class SellReturn: public Strategy {
    int fulfill;
    int reduce;
public:
    SellReturn(int f, int r): fulfill(f), reduce(r){}
    double GetResult(double original) override {
        return original - (int(original) / fulfill) * reduce;
    }
};
class SellContext{
    Strategy* s;
public:
    enum type{
        NORMAL,
        DISCOUNT,
        RETURN
    };
    template<typename ...Ts>
    SellContext(type t, Ts... args){
        switch(t){
        case NORMAL:
            s = new SellNormal();
            break;
        case DISCOUNT:
            // double
            s = new SellDiscount(args...);
            break;
        case RETURN:
            // int, int
            s = new SellReturn(args...);
            break;
        }
    }
    double getResult(double original){
        return s->GetResult(original);
    }
};
    
int main(){
    auto c1 = new SellContext(SellContext::type::NORMAL);
    auto c2 = new SellContext(SellContext::type::DISCOUNT, 0.8);
    auto c3 = new SellContext(SellContext::type::RETURN, 300, 100);
    cout << c1->getResult(1000);
    cout << c2->getResult(1000);
    cout << c3->getResult(1000);
}

它告诉我 C++ 找不到合适的构造函数。我应该如何优雅地处理这个问题?我知道我可以通过使用不同参数重载构造函数来实现这一点。但是这样是不是太冗长了?

报错如下

s

trategy_pattern_with_simple_factory.cpp: In instantiation of 'SellContext::SellContext(SellContext::type, Ts ...) [with Ts = {}]':
strategy_pattern_with_simple_factory.cpp:71:56:   required from here
strategy_pattern_with_simple_factory.cpp:57:17: error: no matching function for call to 'SellDiscount::SellDiscount()'
             s = new SellDiscount(args...);
                 ^~~~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note: candidate: 'SellDiscount::SellDiscount(double)'
     SellDiscount(double r){
     ^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note:   candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(const SellDiscount&)'
 class SellDiscount: public Strategy {
       ^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:20:7: note:   candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(SellDiscount&&)'
strategy_pattern_with_simple_factory.cpp:20:7: note:   candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:61:17: error: no matching function for call to 'SellReturn::SellReturn()'
             s = new SellReturn(args...);
                 ^~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note: candidate: 'SellReturn::SellReturn(int, int)'
     SellReturn(int f, int r): fulfill(f), reduce(r){}
     ^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note:   candidate expects 2 arguments, 0 provided
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(const SellReturn&)'
 class SellReturn: public Strategy {
       ^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:31:7: note:   candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(SellReturn&&)'
strategy_pattern_with_simple_factory.cpp:31:7: note:   candidate expects 1 argument, 0 provided
strategy_pattern_with_simple_factory.cpp: In instantiation of 'SellContext::SellContext(SellContext::type, Ts ...) [with Ts = {double}]':
strategy_pattern_with_simple_factory.cpp:72:63:   required from here
strategy_pattern_with_simple_factory.cpp:61:17: error: no matching function for call to 'SellReturn::SellReturn(double&)'
             s = new SellReturn(args...);
                 ^~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note: candidate: 'SellReturn::SellReturn(int, int)'
     SellReturn(int f, int r): fulfill(f), reduce(r){}
     ^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:35:5: note:   candidate expects 2 arguments, 1 provided
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(const SellReturn&)'
 class SellReturn: public Strategy {
       ^~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:31:7: note:   no known conversion for argument 1 from 'double' to 'const SellReturn&'
strategy_pattern_with_simple_factory.cpp:31:7: note: candidate: 'constexpr SellReturn::SellReturn(SellReturn&&)'
strategy_pattern_with_simple_factory.cpp:31:7: note:   no known conversion for argument 1 from 'double' to 'SellReturn&&'
strategy_pattern_with_simple_factory.cpp: In instantiation of 'SellContext::SellContext(SellContext::type, Ts ...) [with Ts = {int, int}]':
strategy_pattern_with_simple_factory.cpp:73:66:   required from here
strategy_pattern_with_simple_factory.cpp:57:17: error: no matching function for call to 'SellDiscount::SellDiscount(int&, int&)'
             s = new SellDiscount(args...);
                 ^~~~~~~~~~~~~~~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note: candidate: 'SellDiscount::SellDiscount(double)'
     SellDiscount(double r){
     ^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:23:5: note:   candidate expects 1 argument, 2 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(const SellDiscount&)'
 class SellDiscount: public Strategy {
       ^~~~~~~~~~~~
strategy_pattern_with_simple_factory.cpp:20:7: note:   candidate expects 1 argument, 2 provided
strategy_pattern_with_simple_factory.cpp:20:7: note: candidate: 'constexpr SellDiscount::SellDiscount(SellDiscount&&)'
strategy_pattern_with_simple_factory.cpp:20:7: note:   candidate expects 1 argument, 2 provided

首先,Strategy 必须有一个 virtual 析构函数,因为您将通过基础 class 指针销毁实例。

看起来您正在尝试将 type 用作某种标记,并且您不能显式地向构造函数提供模板参数。那将是 class 本身的模板参数 - 而 SellContext 不是 class 模板,因此使用标签是个好主意。不过它们确实需要不同的类型,所以我建议创建单独的标记类型,并将指针变成智能指针。

示例:

#include <memory>

class SellContext{
    std::unique_ptr<Strategy> s; // <- smart pointer

public:
    // tag types and tag instances:
    static constexpr struct NORMAL {} normal_tag{};
    static constexpr struct DISCOUNT {} discount_tag{};
    static constexpr struct RETURN {} return_tag{};

    // The constructors you need. No `switch` needed:
    template<class... Args>
    SellContext(NORMAL, Args&&... args) :
        s(std::make_unique<SellNormal>(std::forward<Args>(args)...)) {}

    template<class... Args>
    SellContext(DISCOUNT, Args&&... args) :
        s(std::make_unique<SellDiscount>(std::forward<Args>(args)...)) {}
    
    template<class... Args>
    SellContext(RETURN, Args&&... args) :
        s(std::make_unique<SellReturn>(std::forward<Args>(args)...)) {}

    double getResult(double original){
        return s->GetResult(original);
    }
};

然后像这样使用它们:

int main(){
    auto c1 = std::make_unique<SellContext>(SellContext::normal_tag);
    auto c2 = std::make_unique<SellContext>(SellContext::discount_tag, 0.8);
    auto c3 = std::make_unique<SellContext>(SellContext::return_tag, 300, 100);
    
    std::cout << c1->getResult(1000) << '\n';
}

Demo

使用 constexpr-if:

#include <type_traits>

class SellContext{
    std::unique_ptr<Strategy> s;

public:
    static constexpr struct NORMAL {} normal_tag{};
    static constexpr struct DISCOUNT {} discount_tag{};
    static constexpr struct RETURN {} return_tag{};

    template<class Tag, class... Args>
    SellContext(Tag, Args&&... args) {
        static_assert(std::is_same_v<NORMAL, Tag> || 
                      std::is_same_v<DISCOUNT, Tag> ||
                      std::is_same_v<RETURN, Tag>);

        if constexpr(std::is_same_v<NORMAL, Tag>)
            s = std::make_unique<SellNormal>(std::forward<Args>(args)...);
        if constexpr(std::is_same_v<DISCOUNT, Tag>)
            s = std::make_unique<SellDiscount>(std::forward<Args>(args)...);
        if constexpr(std::is_same_v<RETURN, Tag>)
            s = std::make_unique<SellReturn>(std::forward<Args>(args)...);
    }

    double getResult(double original){
        return s->GetResult(original);
    }
};

Demo

由于 Strategy 对象的当前构造函数都不相同,您可以更简单地删除标签:

class SellContext{
    std::unique_ptr<Strategy> s;

public:
    SellContext() : s(std::make_unique<SellNormal>()) {}
    SellContext(double x) : s(std::make_unique<SellDiscount>(x)) {}
    SellContext(double x, double y) : s(std::make_unique<SellReturn>(x, y)) {}

    double getResult(double original){
        return s->GetResult(original);
    }
};
int main(){
    auto c1 = std::make_unique<SellContext>();
    auto c2 = std::make_unique<SellContext>(0.8);
    auto c3 = std::make_unique<SellContext>(300, 100);

    std::cout << c1->getResult(1000) << '\n';
}

Demo