用于将 shared_ptr 向量填充到基础和派生对象的函数模板

Posted

技术标签:

【中文标题】用于将 shared_ptr 向量填充到基础和派生对象的函数模板【英文标题】:Function template to populate a vector of shared_ptr to Base & Derived objects 【发布时间】:2019-08-13 06:01:21 【问题描述】:

考虑以下几点:

#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_barsbars.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);

这里的目标是创建请求数量的BaseDerived 对象,并用shared_ptrs 向这些对象填充std::vector

for 语句出现编译器错误:

错误:“开始”没有依赖于模板的参数 参数,因此“开始”的声明必须可用 [-fpermissive]

如果我将std::iteratorbeginend 一起使用,不幸的是,迭代器对每个push_back 都无效。无论如何,我需要迭代特定次数来填充向量。

这个问题有明显的解决办法吗?

【问题讨论】:

【参考方案1】:

    您不能在std::size_t 上使用range-based for loop。你可以改变

    for(auto &i : numElements) 
    

    for (std::size_t i = 0; i < numElements; i++)  
    

    Derived 没有默认构造函数;您需要将参数传递给它以进行构造,否则 std::make_shared&lt;T&gt;() 将失败。您可以使用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;
    
    

    然后像这样使用它

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

【讨论】:

【参考方案2】:

numElements 是一个 std::size_t 类型,它没有定义 beginend 迭代器(这是为标准容器和用户定义类型定义的,而不是为原始类型定义的),这是 range-based for loop。因此你需要一个经典的for loop

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

或者只是一个while循环:

while(numElements--)

    vec.push_back(std::make_shared<T>());


其次,正如@songyuanyao 所指出的,Derived 类必须有一个default 构造函数才能使MakeVector 工作。你可以默认一个:

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

或最好的类似 @songyuanyao 的答案,为构造函数参数提供额外的 varidic-template-args。

【讨论】:

【参考方案3】:
for(auto &i : numElements)      // Compiler error occurs here.
        vec.push_back(std::make_shared<T>());
    

for 范围是针对容器的。这里 numElements 是一个数字,使用经典的 for 循环。

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

【讨论】:

从技术上讲,它适用于 ranges,即定义了迭代器和 end()begin() 的类型。不一定是容器,基于范围的for循环适用于纯数组,可以适配其他类型【参考方案4】:

您不能对任意类型使用基于范围的 for 循环。该类型应与标准中的定义一起使用,

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

即它应该已经定义了beginend,以及增量、取消引用和比较运算符,这不是普通整数类型的情况。在您的情况下,通常 for 是必需的,除非您想定义描述范围的类型。

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

Ofc,一个人可能很有创意,可以使用几乎任何类型的范围。像这样的东西(这绝不是推荐,只是一个例子):

#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 = Tbegin;
    static const T _end = Tend;

    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 类至少需要一个默认构造函数,或者您的填充方法 MakeVector 应更改为放置正确的值

【讨论】:

以上是关于用于将 shared_ptr 向量填充到基础和派生对象的函数模板的主要内容,如果未能解决你的问题,请参考以下文章

识别已将哪个基类 shared_ptr 传递到超类 shared_ptr 向量中

如何将 shared_ptr 变量推回 C++ 中的 shared_ptr 向量?

将项目从一个向量复制到另一个向量

将派生类型的对象从 python 传递到 C++ 函数时会出现 Boost Python 运行时错误,该函数期望将 shared_ptr 传递给基类型

存储 std::shared_ptr<Foo> 的向量,其中 Foo 是模板类

如何将派生类型的对象放入用于基本类型的向量中?