当前位置: 首页 > 知识库问答 >
问题:

除可变模板参数外,输入参数也无法编译

嵇浩然
2023-03-14

我努力一步一步地编写示例,使其清晰易懂。

此处显示的object BaseCreator公开了一个create函数,该函数使用一个内部类NewObject来分配一个t类型的新对象。new object类的默认方法是使用new运算符的常用方法,但是可以使用专门化来更改它,以使用不同的进程。我们稍后会看到它。

template <typename F>
class BaseCreator
{
public:
    BaseCreator() = default;
    virtual ~BaseCreator() = default;

    // create function 
    template <typename T, typename... Args>
    std::shared_ptr<T> create(Args&&... parameters)
    {
        return std::shared_ptr<T>(NewObject<T, Args...>().get(std::forward<Args>(parameters)...));
    }

private:
    // creator object with default functionality to create a new object
    template <typename T, typename... Args>
    struct NewObject
    {
        T* get(Args&&... parameters)
        {
            return new T(std::forward<Args>(parameters)...);
        }
    };
};

这很好,例如,假设我们有以下对象类型:

struct A
{
    A(int i_i, const std::string& i_s) : i(i_i), s(i_s) {}
    void addVal(int i_i) { i+=i_i; }
    int i = 0;
    std::string s;
};

struct B
{
    B(const std::vector<float>& i_v) : v(i_v) {}
    void addVal(int i_i) { v.push_back((float)i_i); }
    std::vector<float> v;
};

我们可以很容易地创建它们,例如:

// declare type for a creator identity 
struct Group111 {};

// creator object
BaseCreator<Group111> cCreator111;

// creating A and B
std::shared_ptr<A> spA = cCreator111.create<A>(5, "Hello");
std::shared_ptr<B> spB = cCreator111.create<B>(std::vector<float>({ 0.5f,0.1f,0.7f,0.9f }));

我们还可以为特定对象和特定类型的创建者声明特定的创建方法。这里举例说明使用组222的创建者创建对象A的专门化:

// declare type for a creator identity 
struct Group222 {};

// specialize NewObject for BaseCreator<Group222> for creating A
template<> 
template< typename... Args> 
struct BaseCreator<Group222>::NewObject<A, Args...>
{
    A* get(int i_factor, int i_i, const std::string& i_s)
    {
        A* p = new A(i_i, i_s);
        p->i *= i_factor;
        return p;
    }
};

并且使用Group222类型的创建者创建对象A将具有与默认方法不同的方法。

// creator object
BaseCreator<Group222> cCreator222;

// creating A with creator of Group222 needs now additional input argument 
std::shared_ptr<A> spA = cCreator222.create<A>(3, 5, "Hello");

// creating A with creator of Group222 is the same as creating using creator cCreator111
std::shared_ptr<B> spB = cCreator222.create<B>(std::vector<float>({ 0.5f,0.1f,0.7f,0.9f }));

现在是问题:)

现在,我想派生一个新的BaseCreator来拥有下面的一个,如您所见,它具有代理创建函数,并使用包中的额外整数调用其基类1:

class Creator333 : public BaseCreator<Creator333>
{
public:
    Creator333() = default;
    virtual ~Creator333() = default;

    // creator
    template <typename T, typename... Args>
    std::shared_ptr<T> create(Args&&... parameters)
    {
        int adder = 5; // just for the example
        return std::shared_ptr<T>(BaseCreator<Creator333>::create<T>(adder, std::forward<Args>(parameters)...));
    }

};

它配备了专门的BaseCreator。

template<> 
template<typename T, typename... Args> 
struct BaseCreator<Creator333>::NewObject<T, Args...>
{
    T* get(int i_adder, Args&&... parameters)
    {
        T* p = new T(std::forward<Args>(parameters)...);
        p->addVal(i_adder);
        return p;
    }
};

如果我尝试创建一个对象,让我们使用这个Creator333说B:

Creator333 cCreator333;

// creation of B using Creator333 
std::shared_ptr<B> spB3 = cCreator333.create<B>(std::vector<float>({ 0.1f,0.2f}));

我将得到一个编译器错误:

error C2660: 'BaseCreator<Creator333>::NewObject<T,int &,_Ty>::get': function does not take 2 arguments
1>        with
1>        [
1>            T=B,
1>            _Ty=std::vector<float,std::allocator<float>>
1>        ]

但这很奇怪,因为该函数确实应该得到两个参数,如下所示:

>

  • Create333::create - 获取一个包含一个元素的包 std::vector({ 0.1f,0.2f})

    它调用其基本计算器<code>BaseCreator

    BaseCreator

    那么问题是什么,怎么解决呢?

    谢谢


  • 共有1个答案

    慕容修伟
    2023-03-14

    以下是一个解决方案,但不是答案,这意味着代码有效,但我不明白为什么这个工作,而问题中的那个不起作用。因此,如果有人可以描述原因,我相信这将有助于许多人更深入地了解这个主题。

    感谢@Kuba comment,我根据他建议的单词进行了搜索,并在这个stackoverflow问题中找到了一个代码,启发了我以下解决方案:

    我为Creator333添加了一个Helper,如下所示。它有助于将输入包拆分为所需的两部分:附加整数和来自应用程序代码的原始包。但我还必须将特殊的创建代码移入其中。

    class Creator333 : public BaseCreator<Creator333>
    {
    public:
        Creator333() = default;
        virtual ~Creator333() = default;
    
        // creator
        template <typename T, typename... Args>
        std::shared_ptr<T> create(Args&&... parameters)
        {
            int adder = 5; // just for the example
            return std::shared_ptr<T>(BaseCreator<Creator333>::create<T>(adder, std::forward<Args>(parameters)...));
        }
    
        // helper class for splitting given pack into  desired two parts: 
        // the additional integer and the original pack came from the application code
        template<typename T, typename FirstTypeT, typename... Args>
        struct Helper
        {
            T* get(FirstTypeT i_adder, Args&&... parameters)
            {
                T* p = new T(std::forward<Args>(parameters)...);
                p->addVal(i_adder);
                return p;
            }
        };
    };
    

    专门化< code>BaseCreator

    template<> 
    template<typename T, typename... Args>
    struct BaseCreator<Creator333>::NewObject<T, Args...>
    {
        T* get(Args&&... parameters)
        {
            return Creator333::Helper<T,Args... >().get(std::forward<Args>(parameters)...);
        }
    };
    
     类似资料:
    • 当我编译时,我得到了这些错误:

    • 以下是我的代码:< br > 一开始我只是重载了函数,发现有很多类似的代码。所以我正在考虑使用可变参数模板来获得更好的设计。(如果两个重载函数相似,如何做出更好的设计) 但是我不知道为什么会有错误:< br > main.cpp:27: 8:错误:没有匹配函数调用'getChar'ch=getChar(1, std::forward(str)...); 主要的cpp:37:2:注意:在函数模板专门

    • 要解决的问题: 怎么创建一个拥有1个、2个或者更多的初始化器的类? 怎么避免创建一个实例而只拷贝部分的结果? 怎么创建一个元组? 最后的问题是关键所在:考虑一下元组!如果你能创建并且访问一般的元组,那么剩下的问题也将迎刃而解。 这里有一个例子(摘自“可变参数模板简述(A brief introduction to Variadic templates)”(参见参考)),要构建一个广义的、类型安全的

    • 在C++11之前,类模板和函数模板只能含有固定数量的模板参数。C++11增强了模板功能,允许模板定义中包含0到任意个模板参数,这就是可变参数模板。可变参数模板的加入使得C++11的功能变得更加强大,而由此也带来了许多神奇的用法。 可变参数模板 可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename或class后面带上省略号...: template<ty

    • 受这个答案的启发,我生成了这段代码,其输出取决于编译器: 如果使用 GCC 11 编译,调用

    • 我希望打印函数根据“值”的类型来做不同的事情。