【问题标题】:input parameter in addition to variadic template arguments failed to compile除了可变参数模板参数之外的输入参数无法编译
【发布时间】:2021-05-06 00:33:45
【问题描述】:

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

这里显示的对象 BaseCreator 公开了一个创建函数,该函数使用内部类 NewObject 来分配类型 T 的新对象。类 NewObject 默认方法是使用 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 }));

我们还可以为特定对象和特定类型的创建者声明特定的创建方法。这里例如使用 Group222 的创建者创建对象 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 以具有以下功能,如您所见,它具有代理创建函数并调用其基类之一,并为包添加额外的整数:

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&lt;Creator333&gt;::NewObject,它应该为任何对象处理这个额外的整数(在这个例子中 - 假设对象有一个名为 addVal() 的成员函数)

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;
    }
};

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

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>        ]

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

  1. Create333::create - 得到一个包含一个元素的包 std::vector({ 0.1f,0.2f})

  2. 它用额外的整数调用它的基本类BaseCreator&lt;Create333&gt;::create,因此 BaseCreator&lt;Create333&gt;::create 得到一个包含两个元素的包:int(5) 和 std::vector({ 0.1f,0.2f})

  3. BaseCreator&lt;Create333&gt;::create`` calls BaseCreator::NewObject.get``` 包的第一个元素用于 get(adder,包的另一个元素用于 get(Args ...

那么问题是什么以及如何解决呢?

谢谢

【问题讨论】:

  • 问题在于重载解决方案并没有像您认为的那样工作,或者,也许更重要的是,您认为存在重载解决方案,但看起来并没有(或反之亦然)。我不认为这个设计会像显示的那样工作,但我只是看了一眼。看起来是一个非常熟悉的问题。我确定这个问题是重复的。谷歌搜索类似重载解析和模板参数包之类的东西。
  • 感谢@Kubahasn'tforgottenMonica,您的建议有所帮助 - 请参阅下面我的回答,但如果您了解原因,它将使其变得完美。

标签: c++ c++11 variadic-templates template-specialization


【解决方案1】:

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

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

我为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;
        }
    };
};

专业化BaseCreator&lt;Creator333&gt;::NewObject 本身现在只需要使用Helper,如图所示:

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)...);
    }
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 2019-07-27
    • 1970-01-01
    • 2013-09-22
    • 1970-01-01
    相关资源
    最近更新 更多