我有一个函数正在查找自定义列表实现中的元素。为了使它成为const-correct,我可以做到这种整齐的重载,甚至可以使用单行来重用相同的实现,因此不必复制逻辑。
const MyObject* findObject(const MyList& list)
{
//...
}
MyObject* findObject(MyList& list)
{
return const_cast<MyObject*>(findObject(const_cast<const MyList&>(list)));
}
问题是,一旦我需要在向量中返回多个元素指针,除了像reinterpret_cast之类的unsave / non-portable hacks,我该怎么办?
std::vector<const MyObject*> findObject(const MyList& list)
{
//...
}
std::vector<MyObject*> findObject(MyList& list)
{
// this is sth I'm looking for:
const_element_cast<std::vector<MyObject*>>( findObject( const_cast<const MyList&>(list)) );
}
答案 0 :(得分:2)
最简单的解决方案是忘记const_cast
并明确地实现两个重载。 这是标准库实现的目的。
例如,std::vector
的{{1}}版本为const
,非const
版本为operator[]
。 VC ++和GCC都有该运算符的重复实现(分别参见include/vector
或stl_vector.h
文件);不会采用任何const_cast
技巧来复制行为。
现在,如果你的findObject
实现非常庞大和复杂,那么首选应该是让它变得更简单。作为临时解决方法,您可以考虑使用decltype
尾随返回类型来获取正确的const
或非{{1通过参数返回类型。这是一个简单的例子:
const
示例输出(取决于#include <iostream>
#include <vector>
#include <typeinfo> // just to demonstrate what's going on
// simple example data structure:
struct MyObject {};
struct MyList
{
MyObject elements[3] = { MyObject {}, MyObject {}, MyObject {} };
};
namespace internal
{
// &list.elements[0] will be either MyObject const* or MyObject*
template <class ConstOrNonConstList>
auto doFindObject(ConstOrNonConstList& list) -> std::vector<decltype(&list.elements[0])>
{
// let's say there was an immensely complicated function here,
// with a lot of error potential and maintenance nightmares
// lurking behind a simple copy & paste solution...
// just to demonstrate what's going:
std::cout << typeid(decltype(&list.elements[0])).name() << "\n";
std::vector<decltype(&list.elements[0])> result;
for (auto&& element : list.elements)
{
result.push_back(&element);
}
return result;
}
}
std::vector<const MyObject*> findObject(const MyList& list)
{
std::cout << "const version\n";
return internal::doFindObject(list);
}
std::vector<MyObject*> findObject(MyList& list)
{
std::cout << "non-const version\n";
return internal::doFindObject(list);
}
int main()
{
MyList list1;
MyList const list2;
auto obj1 = findObject(list1);
auto obj2 = findObject(list2);
}
在您的实现中产生的名称类型):
typeid
但坦率地说,我不会这样做。它看起来过于设计,而且可能有点过于聪明。过于聪明的代码很少是一个好主意,因为它会让人感到困惑。
答案 1 :(得分:0)
选项1
如果您愿意支付循环+复制价格(与复制粘贴代码相比):
std::vector<const thing*> c_vec = static_cast<const me*>(this)->my_const_func();
std::vector<thing*> ret;
for (const thing* t : c_vec) {
// Yes you can do this, since this func isn't const
// so you are guaranteed to be inside non-const context.
// See Scott Meyers.
ret.push_back(const_cast<thing*>(t));
}
return ret;
选项2
[edit]如果您不想支付循环+复制价格,这是另一种选择。
假设您正在使用cpp文件(不是仅标头)。在您的编译单元(.cpp)中,添加模板化的吸气剂。
template <class T, class MyObj>
std::vector<T*> getter_imp(MyObj& obj, int someval) {
assert(someval <= 42);
std::vector<T*> ret;
for (auto& i : obj._my_vec) {
if (i < someval) {
ret.push_back(&i);
}
}
return ret;
}
为了保持环境的清洁和“隐藏”,您可以在课堂上将此功能作为朋友。
template <class T, class MyObj>
friend std::vector<T*> getter_imp(MyObj&, int);
现在,您可以从const和非const成员函数中调用getter了。没有复制粘贴(又名我们生命的祸根)。
std::vector<const int*> get(int v) const {
return getter_imp<const int>(*this, v);
}
std::vector<int*> get(int v) {
return getter_imp<int>(*this, v);
}
这是怎么回事?
您正在模板参数中编码const
。假设您是通过const
函数T = const int
和MyObj = const your_class
进行调用的。在函数内部,const
也被推导为auto&
。
从非const
上下文调用时,模板参数和auto&
不是const
。这样就可以了。