假设我有一个Storage的Objects,它有一个方法来聚合指向向量中某些Objects的指针。如下所示:
class Storage
{
public:
std::vector<Object*> aggregate_some_objects(); // non-const version
std::vector<const Object*> aggregate_some_objects() const; // const version
private:
std::unordered_map<size_t, Object> m_objects; // data is stored
// by-value in a non-vector container
}通常,在实现const +non方法对时,可以避免复制粘贴,方法是在const_cast的帮助下调用另一个方法对中的一个。但是,在这里,这是不可能的,因为方法的返回类型不同。
避免复制粘贴的最简单的方法是从非const版本调用const版本,并使用返回的std::vector<const T*>填充单独的std::vector<T*>。然而,这将导致至少2个堆分配(每个向量一个)。我要避免与第二个向量有关的分配。
我想知道有没有办法写这样的东西
template <typename T>
std::vector<T*> remove_const_from_vector_of_ptrs(std::vector<const T*>&& input)
{
std::vector<T*> ret;
// do some magic stuff here that does not involve
// more memory allocations
return ret;
}因此,允许写
std::vector<const Object*> Storage::aggregate_some_objects() const
{
// non-trivial implementation
}
std::vector<Object*> Storage::aggregate_some_objects()
{
auto objects = const_cast<const Storage*>(this)->aggregate_some_objects();
return remove_const_from_vector_of_ptrs(std::move(objects));
}在std::vector (例如,std::unique_ptr )中没有允许传输内存所有权的‘reason’方法--而且有一个很好的理由,所以我认为这是不可能的。
我还理解,如果可能的话,这将是一个危险的操作,应该避免,就像const_cast一样。但是在这种情况下小心使用似乎比复制粘贴更有益。
编辑:对我所说的“额外”分配进行了澄清,并将Storage::aggregate_objects()改为Storage::aggregate_some_objects(),以更好地表明这些方法的实现比基于范围的循环更复杂,因此希望避免复制粘贴实现。
发布于 2016-06-11 13:23:27
简短的回答是:不。std::vector<Object*>和std::vector<const Object*>是两个不同的独立类。它们之间的区别就像class A和class B的区别一样。人们经常认为,仅仅因为他们都是从std::vector开始的,他们之间就有某种程度的联系。这是不正确的,因此,没有办法将其中一个转变为另一个,“就位”。这些向量类中的每一个都拥有它们相应的内部data(),并且不愿意放弃给其他一些奇怪的类。
很长的答案仍然是否定的,但在许多情况下,为了避免手工代码重复,可以解决这个问题。事实上,在大多数这类情况下,代码重复是不可避免的,最好的方法就是避免人工代码重复。
一种常见的方法是为单个共享的私有模板提供常量类方法和可变类方法:
// Header file:
class Storage {
public:
std::vector<const Object*> aggregate_objects() const;
std::vector<Object*> aggregate_objects();
private:
template<typename v_type> void make_aggregate_objects(v_type &v) const;
};
// In the translation unit:
template<typename v_type> void Storage::make_aggregate_objects(v_type &v) const
{
// Now, create 'v' here... v.reserve(), v.push_back(), etc...
}
std::vector<const Object*> Storage::aggregate_objects() const
{
std::vector<const Object *> v;
make_aggregate_objects(v);
return v;
}
std::vector<Object*> Storage::aggregate_objects()
{
std::vector<const Object *> v;
make_aggregate_objects(v);
return v;
}编译器仍然会生成两个几乎相同的代码块,但至少不是由您来完成所有的输入。
另一种类似的方法是将lambda传递给模板函数,而不是传递向量对象,而私有模板函数使用lambda函数作为回调来构造返回的向量。通过一些类型擦除和std::function的一些帮助,可以将私有类方法转换为普通方法,而不是模板方法。
发布于 2016-06-11 13:54:20
如果不重新分配内存和复制指针,就无法将std::vector<const Object*>转换为std::vector<Object*>,因为std::vector是一个容器,拥有它的内存。
在这种情况下,使用reinterpret_cast可能有效,但这是未定义的行为,并且取决于std::vector的实现。
std::vector<const Object*> const_vec = ...;
std::vector<Object*>& vec = reinterpret_cast<std::vector<Object*>&>(const_vec);避免const_cast或不必要分配的解决方案是第三个模板函数:
template<typename Stor>
static auto Storage::aggregate_objects_(Stor&)
-> std::vector<std::conditional_t<std::is_const<Stor>::value, const Object*, Object*>>
{
...
}Stor可以是Storage或const Storage。
然后将aggregate_objects()实现为:
std::vector<const Object*> Storage::aggregate_objects() const {
return aggregate_objects_(*this);
}
std::vector<Object*> Storage::aggregate_objects() {
return aggregate_objects_(*this);
}发布于 2016-06-11 14:33:12
您的函数按值返回,所以始终要分配--您说的是什么“额外分配”?
如果您只是在内部存储vector<Object*>,那么解决您的问题就很简单了:
std::vector<Object*> Storage::aggregate_objects()
{ return m_data; };
std::vector<const Object*> Storage::aggregate_objects() const
{ return std::vector<const Object*>(m_data.begin(), m_data.end()); }编辑:回答您更新的问题:
您不应该仅仅为了避免函数体的复制和粘贴而编写错误的代码!
没有必要复制函数体,也不需要用危险或危险的转换编写糟糕的代码,只需使用两个函数都调用的模板,如的答案所示。
https://stackoverflow.com/questions/37764052
复制相似问题