简体   繁体   English

用于将shared_ptr的向量填充到Base和Derived对象的函数模板

[英]Function template to populate a vector of shared_ptr to Base & Derived objects

Consider the following: 考虑以下:

#include <memory>
#include <utility>
#include <vector>

class Base
{
public:
    Base()
        : x(0)
    {}
    int x;
    virtual ~Base() = default;
};

class Derived : public Base
{
public:
    Derived(double z0)
        : Base{}
        , z{ z0 }
    {}
    double z;
};

template<class T> // T might be either a Base or a Derived class.
std::vector<std::shared_ptr<Base>> MakeVector(std::size_t numElements)
{
    std::vector<std::shared_ptr<Base>> vec;
    for(auto &i : numElements) {     // Compiler error occurs here.
        vec.push_back(std::make_shared<T>());
    }
    return vec;
}

class Foo
{
public:
    Foo(std::size_t num_elements,
        std::vector<std::shared_ptr<Base>> bars = {})
    : m_bars{bars.empty() ? MakeVector<Base>(num_elements) : std::move(bars)}
    {}

    std::vector<std::shared_ptr<Base>> m_bars;
};

int main()
{
    const std::size_t foo1Size = 4;
    const std::size_t foo2Size = 5;

    // Create a vector of shared_ptr to 4 Base objects:
    Foo foo1 {foo1Size};

    // Create a vector of shared_ptr to 5 Derived objects:
    Foo foo2 {foo2Size, MakeVector<Derived>(foo2Size)};
}

The objective here is to create a requested number of Base or Derived objects, and populate a std::vector with shared_ptr s to those objects. 这里的目标是创建一个请求数量的BaseDerived对象,并将一个带有shared_ptrstd::vector填充到这些对象。

I am getting a compiler error on the for statement: 我在for语句上遇到编译器错误:

error: there are no arguments to 'begin' that depend on a template parameter, so a declaration of 'begin' must be available [-fpermissive] 错误:“begin”没有依赖于模板参数的参数,因此'begin'的声明必须可用[-fpermissive]

If I use a std::iterator with begin and end , unfortunately the iterator becomes invalid with each push_back . 如果我使用带有beginendstd::iterator ,不幸的是迭代器对每个push_back变得无效。 And I need to iterate a specific number of times, anyway, to populate the vector. 无论如何,我需要迭代特定次数来填充向量。

Is there an obvious solution to this problem? 这个问题有明显的解决方案吗?

  1. You can't use range-based for loop on an std::size_t . 您不能在std::size_t上使用基于范围的for循环 You could change 你可以改变

     for(auto &i : numElements) { 

    to

     for (std::size_t i = 0; i < numElements; i++) { 
  2. Derived doesn't have default constructor; Derived没有默认构造函数; you need to pass the argument to it for constructing, otherwise std::make_shared<T>() would fail. 你需要将参数传递给它进行构造,否则std::make_shared<T>()会失败。 You can change MakeVector to the following with parameter pack : 您可以使用参数包MakeVector更改为以下内容:

     template<class T, class... Types> // T might be either a Base or a Derived class. std::vector<std::shared_ptr<Base>> MakeVector(std::size_t numElements, Types... args) { std::vector<std::shared_ptr<Base>> vec; for (std::size_t i = 0; i < numElements; i++) { vec.push_back(std::make_shared<T>(args...)); } return vec; } 

    then use it like 那就像使用它一样

     // Create a vector of shared_ptr to 5 Derived objects: Foo foo2 {foo2Size, MakeVector<Derived>(foo2Size, 42)}; 

The numElements is a std::size_t type which has no begin and end iterator defined (which is defined for standard containers and user-defined types, not for primitive types), which are required for a range-based for loop . numElements是一个std::size_t类型,它没有定义beginend迭代器(为标准容器和用户定义的类型定义,而不是为基本类型定义),这是基于范围的for循环所必需的。 Therefore you need either a classical for loop 因此,您需要一个经典的for循环

for(std::size_t index{ 0 }; index  < numElements; index ++) 
{ 
    vec.push_back(std::make_shared<T>());
}

or simply a while loop: 或者只是一个while循环:

while(numElements--)
{
    vec.push_back(std::make_shared<T>());
}

Secondly, as @songyuanyao pointed out, Derived class must have a default constructor for the MakeVector to work. 其次,正如@songyuanyao指出的那样, Derived类必须有一个default构造函数才能使MakeVector工作。 You can default one like: 您可以默认如下:

Derived() = default;
// or
// Derived(double z0 = 0.0): Base{}, z{ z0 } {}

or the best like in @songyuanyao 's answer , provide an extra varidic-template-args for the constructor parameters. @songyuanyao 的答案中最好 ,为构造函数参数提供额外的varidic-template-args。

for(auto &i : numElements) {     // Compiler error occurs here.
        vec.push_back(std::make_shared<T>());
    }

The for range is for container. 适用范围适用于容器。 Here numElements is a number, use a classical for loop. 这里numElements是一个数字,使用经典的for循环。

for(std::size_t i = 0; i < numElements; i++) { 
        vec.push_back(std::make_shared<T>());
    }

You can't use a range-based for loop on arbitrary type. 您不能在任意类型上使用基于范围的for循环。 The type should work with definition from standard, 该类型应符合标准的定义,

// for ( range_declaration : range_expression ) loop_statement

{
  auto && __range = range_expression ; 
  for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) 
  { 
    range_declaration = *__begin; 
    loop_statement 
  } 
}

ie it should have defined begin , end , as well as increment, dereference and comparison operators, which isn't a case for trivial integral types. 即它应该定义beginend ,以及增量,解除引用和比较运算符,这不是普通整数类型的情况。 In your case usual for is required, unless you want to define a type that describes range. 在您需要的情况下,除非您想要定义描述范围的类型。

for (auto i = 0; i < numElements; ++i)

Ofc, one may be creative and use range for with just about any type. Ofc,一个人可能很有创意并且几乎可以使用任何类型的范围。 Something like this (this is no way a recommendation, just an example): 像这样的东西(这不是一个推荐,只是一个例子):

#include <utility> 
#include <iostream>

// Those should be in same namespace
template<class T, template<typename,T,T> class iT, T _b, T _e>
iT<T,_b,_e> begin(iT<T,_b,_e> v)
{
    return iT<T,_b,_e> {_b};
}

template<class T, template<typename,T,T> class iT, T _b, T _e>
iT<T,_b,_e> end(iT<T,_b,_e> v)
{
    return iT<T,_b,_e> {_e};
}

template<class T, T begin, T end> 
struct range {
    static const T _begin = T{begin};
    static const T _end = T{end};

    T value;

    range& operator++() { //prefix
        ++value;
        return *this;
    }

    T operator+(T inc) {
        return range<T,begin,end>{value + inc};
    }

    T operator-(T inc) {
        return range<T,begin,end>{value - inc};
    }

    T operator*() {return value;}

    bool operator != (range arg) { return value != arg.value; }
};

template<class T, T _b, T _e>
range<T,_b,_e> operator++(range<T,_b,_e> &v, int) //postfix
{ 
        range<T,_b,_e> result {v};
        ++v;
        return result;
}

int main()
{
    typedef range<size_t, 3, 10> SomeRange;

    for(auto i : SomeRange())
    {
        std::cout << i << std::endl;
    }
}

Derived class needs at least a default constructor, or your populating method MakeVector should be changed to emplace proper values Derived类至少需要一个默认构造函数,或者应该将您的填充方法MakeVector更改为赋予正确的值

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 通过矢量 <shared_ptr<Derived> &gt;到期望向量的函数 <shared_ptr<Base> &gt; - passing vector<shared_ptr<Derived>> to a function expecting vector<shared_ptr<Base>> 如何转换 shared_ptr <vector<derived class> > 到一个 shared_ptr <vector<base class> >>? </vector<base></vector<derived> - How to cast a shared_ptr<vector<derived class>> to a shared_ptr<vector<base class>>>? 将 shared_ptr 的向量复制到抽象基的派生类 - Copy vector of shared_ptr to derived classes of abstract base 类型推导失败:将派生模板的shared_ptr转换为基本模板作为函数参数 - type deduction fail: shared_ptr of derived template to base template as function argument 返回 std::shared_ptr<Derived> 来自 std::shared_ptr 的向量<Base> - returning std::shared_ptr<Derived> from vector of std::shared_ptr<Base> shared_ptr的 <Base> 和派生类中的对象 - shared_ptr<Base> and objects from derived classes 使用shared_ptr的派生类对象的向量 - Vector of derived class objects using shared_ptr 将派生类shared_ptr作为参数传递给想要基类shared_ptr的函数 - pass derived class shared_ptr as parameter to function that wants base class shared_ptr 传递shared_ptr <Base> 作为shared_ptr <Derived> - Passing shared_ptr<Base> as shared_ptr<Derived> 传递 shared_ptr<Derived> 作为 shared_ptr<Base> - Passing shared_ptr<Derived> as shared_ptr<Base>
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM