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

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

考虑以下几点:

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

这里的objective是创建请求数量的BaseDerived个对象,并用shared_ptr填充一个std::vector到这些对象.

我在 for 语句中遇到编译器错误:

error: there are no arguments to ‘begin’ that depend on a template parameter, so a declaration of ‘begin’ must be available [-fpermissive]

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

这个问题有明显的解决方案吗?

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

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

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

numElements 是一个 std::size_t 类型,它没有定义 beginend 迭代器(它是为标准容器和用户定义类型定义的,而不是为原始类型),这是 range-based for loop. Therefore you need either a classical 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 class必须有一个default构造函数用于MakeVector工作。您可以默认一个:

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

@songyuanyao 's answer 中最好的,为构造函数参数提供额外的 varidic-template-args。

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

    for(auto &i : numElements) {
    

    for (std::size_t i = 0; i < numElements; i++) { 
    
  2. Derived 没有默认构造函数;您需要将参数传递给它进行构造,否则 std::make_shared<T>() 将失败。您可以使用 parameter packMakeVector 更改为以下内容:

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

您不能对任意类型使用基于范围的 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 = 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 至少需要一个默认构造函数,或者您的填充方法 MakeVector 应该更改为放置适当的值