用于将 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);
这里的目标是创建请求数量的Base
或Derived
对象,并用shared_ptr
s 向这些对象填充std::vector
。
for
语句出现编译器错误:
错误:“开始”没有依赖于模板的参数 参数,因此“开始”的声明必须可用 [-fpermissive]
如果我将std::iterator
与begin
和end
一起使用,不幸的是,迭代器对每个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<T>()
将失败。您可以使用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
类型,它没有定义 begin
和 end
迭代器(这是为标准容器和用户定义类型定义的,而不是为原始类型定义的),这是 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
即它应该已经定义了begin
、end
,以及增量、取消引用和比较运算符,这不是普通整数类型的情况。在您的情况下,通常 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 传递给基类型