如何const_cast一个const指针向量到非常量指针的向量?

时间:2016-03-24 18:26:30

标签: c++ pointers vector const-cast

我有一个函数正在查找自定义列表实现中的元素。为了使它成为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)) );
}

2 个答案:

答案 0 :(得分:2)

最简单的解决方案是忘记const_cast并明确地实现两个重载。 这是标准库实现的目的。

例如,std::vector的{​​{1}}版本为const,非const版本为operator[]。 VC ++和GCC都有该运算符的重复实现(分别参见include/vectorstl_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 intMyObj = const your_class进行调用的。在函数内部,const也被推导为auto&

从非const上下文调用时,模板参数和auto&不是const。这样就可以了。