c++ template使用默认值的template函数声明

mwg9r5ms  于 2023-01-28  发布在  其他
关注(0)|答案(1)|浏览(151)

这是我昨天做的this post的后续,它完美地回答了我的问题,但是我意识到我的问题比我最初问的要复杂一些。
比如,我有以下代码:

#include <iostream>
#include <typeinfo>
#include <ratio>
#include <vector>
#include <memory>

struct IStrategy
{
    virtual void Declare() = 0;
    virtual void Build() = 0;
};

template<typename T>
struct IStrategyStorer : public IStrategy
{
    using StoredType=T;
    T& stored;

    IStrategyStorer(T& i_Input):
        stored{i_Input}
    {}
};

template<typename T>
struct BasicStrategy : public IStrategyStorer<T>
{
    BasicStrategy(T& i_Input):
        IStrategyStorer<T>(i_Input)
    {
    }
    
    void Declare()
    {
        std::cout << "Declaring type : " << typeid(T).name() << " as parameter (Basic Strategy)" << std::endl;
        IStrategyStorer<T>::stored = 0;
    }
  
    void Build()
    {
        std::cout << "Building type (Basic Strategy) : " << typeid(T).name() << std::endl;
        IStrategyStorer<T>::stored = 1;
    }
};

template<typename RATIO, typename T>
struct ImposedValueStrategy : public IStrategyStorer<T>
{
    ImposedValueStrategy(T& i_Input):
        IStrategyStorer<T>(i_Input)
    {
    }
    
    void Declare()
    {
        std::cout << "Declaring type : " << typeid(T).name() << " as parameter (ImposedValue Strategy)" << std::endl;
        IStrategyStorer<T>::stored = 0;
    }
  
    void Build()
    {
        std::cout << "Building type (ImposedValue Strategy): " << typeid(T).name() << std::endl;
        IStrategyStorer<T>::stored = ((double) RATIO::num) / ((double) RATIO::den);
    }
};

template<typename Ratio, typename PARENT_STRATEGY>
struct RatioStrategy : public PARENT_STRATEGY
{
    using Type = typename PARENT_STRATEGY::StoredType;
    
    RatioStrategy(Type& i_Input):
        PARENT_STRATEGY(i_Input)
    {
    }
    
    void Declare()
    {
        std::cout << "Transfering type declaration (currently in RatioStrategy)" << std::endl;
        PARENT_STRATEGY::Declare();
    }
  
    void Build()
    {
        PARENT_STRATEGY::Build();   
        std::cout << "Applying ratio multiplication" << std::endl;
        PARENT_STRATEGY::stored *= ((double)Ratio::num)/((double)Ratio::den);
    }
};

template<typename Ratio, typename PARENT_STRATEGY>
struct SumStrategy : public PARENT_STRATEGY
{
    using Type = typename PARENT_STRATEGY::StoredType;
    
    SumStrategy(Type& i_Input):
        PARENT_STRATEGY(i_Input)
    {
    }
    
    void Declare()
    {
        std::cout << "Transfering type declaration (currently in SumStrategy)" << std::endl;
        PARENT_STRATEGY::Declare();
    }
  
    void Build()
    {
        PARENT_STRATEGY::Build();   
        std::cout << "Applying ratio sum" << std::endl;
        PARENT_STRATEGY::stored += ((double)Ratio::num)/((double)Ratio::den);
    }
};

template<typename Ratio1, typename Ratio2, typename PARENT_STRATEGY>
struct AffineStrategy : public PARENT_STRATEGY
{
    using Type = typename PARENT_STRATEGY::StoredType;
    
    AffineStrategy(Type& i_Input):
        PARENT_STRATEGY(i_Input)
    {
    }
    
    void Declare()
    {
        std::cout << "Transfering type declaration (currently in AffineStrategy)" << std::endl;
        PARENT_STRATEGY::Declare();
    }
  
    void Build()
    {
        PARENT_STRATEGY::Build();   
        std::cout << "Applying affine transformation" << std::endl;
        PARENT_STRATEGY::stored = PARENT_STRATEGY::stored * ((double)Ratio1::num)/((double)Ratio1::den) + ((double)Ratio2::num)/((double)Ratio2::den);
    }
};

std::vector<IStrategy*> StrategiesList{}; // This stores the strategies for delayed building!

void buildStrategies()
{
    for(auto& strat : StrategiesList)
    {
        strat -> Build();
    }
}

template<template<typename> class Strategy = BasicStrategy, typename T>
void generic_parameter(T& i_Data)
{
    Strategy<T>* strat = new Strategy<T>(i_Data);
    StrategiesList.push_back((IStrategy*)strat);
    strat->Declare();
}

template<class Strategy, typename T>
void generic_parameter(T& i_Data)
{
    Strategy* strat = new Strategy(i_Data);
    StrategiesList.push_back((IStrategy*)strat);
    strat->Declare();
}

int main()
{
    double d = 24;
    
    generic_parameter(d);
    generic_parameter<ImposedValueStrategy<std::ratio<4,3>, double>>(d);
    generic_parameter<RatioStrategy<std::ratio<2,1>, SumStrategy<std::ratio<3,1>, BasicStrategy<double>>>>(d);
    generic_parameter<AffineStrategy<std::ratio<2, 1>, std::ratio<5, 1>, BasicStrategy<double>>>(d);
    
    buildStrategies();
    
    std::cout << d << std::endl;
    return 0;
}

generic_parameter的第一个重载中使用的template参数允许我定义一个BasicStrategy,而不必指定两次类型(在本例中是intdouble)。
然而,我找不到一种方法来使用相同的原则来推导非违约策略的这种类型,无论是:
1.堆叠策略。例如:* 基本策略&乘以比率 *
1.需要比类型更多的模板参数的更复杂的策略。例如:* 强制价值战略 *
我希望我能在'main()~函数中写的是:

generic_parameter(d); // Uses BasicStrategy : currently works!
generic_parameter<ImposedValueStrategy<std::ratio<4,3>>>(d); // This does not compile
generic_parameter<AffineStrategy<std::ratio<2, 1>, std::ratio<5, 1>, BasicStrategy>>(d); // This does not compile
generic_parameter<RatioStrategy<std::ratio<2,1>, SumStrategy<std::ratio<3,1>, ImposedValueStrategy<std::ratio<3,1>>>>>(d); // This would not compile either

上下文:
我正在一个遗留API(用C & Fortran编写)之上构建一个C11接口,它使用户能够声明所谓的"参数"。API然后存储指向这些参数的指针,然后稍后这些变量将由API填充值。
这对于算术变量和C风格数组非常有效,但现在需求已经改变,C
API必须能够将任何对象声明为参数,因此每个对象都需要一个"策略",描述对象的哪一部分必须向API声明,以及必须如何构建对象,用户可以定义自己的"策略"。
请注意,由于API在声明后会延迟获取数据,因此每个策略都必须拥有对用户数据的引用,以便在数据准备就绪时构建它。此外,还需要能够将策略"堆叠"在彼此的顶部,因为这样可以避免使用临时变量(特别是对于单位转换,这会导致大量错误)。

ozxc1zmp

ozxc1zmp1#

在你的另一个问题中,你已经知道了模板的模板参数。你只是缺少了一个步骤。
假设您有一个类模板

template <typename A,typename B>
struct foo {};

现在你想绑定参数A,然后推导出B,如果你有一个只带一个参数B的模板,这就可以了,所以我们可以把它重写为:

template <typename A>
struct wrapper {
    template <typename B>
    struct foo{};
};

在代码中应用这个方法并引入模板tempalte参数,结果如下(我删除了一些策略,只保留了一些):

#include <iostream>
#include <typeinfo>
#include <ratio>
#include <vector>
#include <memory>

struct IStrategy {
    virtual void Declare() = 0;
    virtual void Build() = 0;
};

template<typename T> struct IStrategyStorer : public IStrategy {
    using StoredType=T;
    T& stored;
    IStrategyStorer(T& i_Input) : stored{i_Input} {}
};

template<typename T> struct BasicStrategy : public IStrategyStorer<T> {
    BasicStrategy(T& i_Input) : IStrategyStorer<T>(i_Input) { }
    void Declare() {
       std::cout << "Declaring type : " << typeid(T).name() << " as parameter (Basic Strategy)" << std::endl;
       IStrategyStorer<T>::stored = 0;
    }  
    void Build() {
        std::cout << "Building type (Basic Strategy) : " << typeid(T).name() << std::endl;
        IStrategyStorer<T>::stored = 1;
    }
};

template <typename RATIO> 
struct RatioLeaves {
    template<typename T> struct ImposedValueStrategy : public IStrategyStorer<T> {
        ImposedValueStrategy(T& i_Input) : IStrategyStorer<T>(i_Input) { }
        void Declare() {
            std::cout << "Declaring type : " << typeid(T).name() << " as parameter (ImposedValue Strategy)" << std::endl;
            IStrategyStorer<T>::stored = 0;
        }
        void Build() {
            std::cout << "Building type (ImposedValue Strategy): " << typeid(T).name() << std::endl;
            IStrategyStorer<T>::stored = ((double) RATIO::num) / ((double) RATIO::den);
        }
    };

};
template <typename RATIO, template <typename> class PARENT_STRATEGY> struct RatioStrategies {
    template <typename T> struct RatioStrategy : public PARENT_STRATEGY<T> {
        using Type = typename PARENT_STRATEGY<T>::StoredType;    
        RatioStrategy(Type& i_Input) : PARENT_STRATEGY<T>(i_Input) {}    
        void Declare() {
            std::cout << "Transfering type declaration (currently in RatioStrategy)" << std::endl;
            PARENT_STRATEGY<T>::Declare();
        }
        void Build() {
            PARENT_STRATEGY<T>::Build();   
            std::cout << "Applying ratio multiplication" << std::endl;
            PARENT_STRATEGY<T>::stored *= ((double)RATIO::num)/((double)RATIO::den);
        }
    };
    template <typename T> struct SumStrategy : public PARENT_STRATEGY<T> {
        using Type = typename PARENT_STRATEGY<T>::StoredType;
        SumStrategy(Type& i_Input): PARENT_STRATEGY<T>(i_Input) { }
        void Declare(){
            std::cout << "Transfering type declaration (currently in SumStrategy)" << std::endl;
            PARENT_STRATEGY<T>::Declare();
        }
        void Build() {
            PARENT_STRATEGY<T>::Build();   
            std::cout << "Applying ratio sum" << std::endl;
            PARENT_STRATEGY<T>::stored += ((double)RATIO::num)/((double)RATIO::den);
        }
    };
};

std::vector<IStrategy*> StrategiesList{}; // This stores the strategies for delayed building!

void buildStrategies()
{
    for(auto& strat : StrategiesList)
    {
        strat -> Build();
    }
}

template<template<typename> class Strategy = BasicStrategy, typename T>
void generic_parameter(T& i_Data)
{
    Strategy<T>* strat = new Strategy<T>(i_Data);
    StrategiesList.push_back((IStrategy*)strat);
    strat->Declare();
}

template<class Strategy, typename T>
void generic_parameter(T& i_Data)
{
    Strategy* strat = new Strategy(i_Data);
    StrategiesList.push_back((IStrategy*)strat);
    strat->Declare();
}

int main()
{
    double d = 24;
    
    generic_parameter(d);
    generic_parameter<RatioLeaves<std::ratio<4,3>>::ImposedValueStrategy>(d);
    generic_parameter< RatioStrategies<std::ratio<2,1>,BasicStrategy>::SumStrategy>(d);
    generic_parameter<RatioStrategies<std::ratio<2,1>,
                            RatioStrategies<std::ratio<3,1>,
                                    RatioLeaves<std::ratio<3,1>>::ImposedValueStrategy
                            >::SumStrategy
                        >::RatioStrategy>(d);
    buildStrategies();
    std::cout << d << std::endl;
    return 0;
}

Live Demo
语法有点笨拙,但我希望总体思路能够清晰,不是提供所有参数并示例化类型,而是只示例化一个 Package 器,并且T的推导保留到“以后”调用generic_parameter时。
我建议使用变量模板而不是继承树。

template <typename source, typename ... transformations>
 struct value {
     typename source::value_type get();
 };

其中get从源中检索值并应用所有transformations。这将避免需要如此深的嵌套,而在需要时嵌套仍然是可能的。

相关问题