const_casting 容器的元素类型

const_casting element type of container

是否有一种有效且安全的方法将 std::vector<const Point*>& 转换为 std::vector<Point*>&

执行 reinterpret_cast<std::vector<Point*>&>(constvec) 可能会正常工作,但可能是未定义的行为。

唯一的标准选项似乎是构建一个新的 std::vector<Point*> 并手动添加每个 const_casted 元素,但程序会不必要地为其分配内存。

编辑: 该程序看起来像这样(简化):

class A {
private:
    int some_data[10];

public:
    template<typename Callback>
    void algo(Callback callback) const {
          std::vector<const int*> values { &some_data[0], &some_data[5], &some_data[3] };
          // Class is const-correct internally, so only have const access to data here.
          // Making it mutable is not easily possible in the real code,
          // as there are constructs similar to iterator/const_iterator in the class hierarchy.
          callback(values);
    }

    template<typename Callback>
    void algo(Callback callback) {
         // Hack to avoid copying the entire algorithm implementation
         auto cb = [&](const std::vector<const int*>& vec) {
             callback(....); // <--- should be const std::vector<int*>
         }
         static_cast<const A*>(this)->algo(cb);
    }
};

另一种选择是在非 const 变体中实现算法,然后 const_cast<A&>(*this).algo() 用于 const 变体。但这似乎更危险,因为 A 对象可能已创建为 const (const A a;) 然后它是 UB.

除了 std::vector 是一个标准库 class 之外,类型 vector<Foo*>vector<Foo const*> 可能有不同的专门化和不同的大小。因此,虽然 reinterpret_cast 通常会起作用,但它在形式上是未定义的行为。无论如何,需要 reinterpret_cast(除了在某些 C 风格的上下文中,例如 Windows API 级别的编程)通常是一个强烈的信号,表明一个人走错了路。

如果有人在 const 和非 const 版本中都需要一种方法的重要代码,一种解决方案是遵循普通函数,例如一个 static 成员函数。

要做到这一点,有一些支持机器很方便:

struct Mutable{};
struct Const{};

template< class Constness, class Type >
struct With_t;

template< class Type > struct With_t<Mutable, Type>{ using T = Type; };
template< class Type > struct With_t<Const, Type>{ using T = Type const; };

template< class Constness, class Type >
using With = typename With_t<Constness, Type>::T;

然后你可以这样写classes:

class Foo
{
private:
    int     something_;

    template< class Constness >
    static auto p( With<Constness, Foo>* that )
        -> With<Constness, int>*
    { return &that->something_; }   // In the real world some non-trivial code here.

public:
    auto p() -> int* { return p<Mutable>( this ); }
    auto p() const -> int const* { return p<Const>( this ); }

    Foo( int const value ): something_( value ) {}
};
template<class A, class B>
struct as_const_as { using type=B; }
template<class A, class B>
struct as_const_as<const A, B> { using type=const B; }
template<class A, class B>
using as_const_as_t=typename as_const_as<A,B>::type;

private:
  template<class Self, class Callback>
  static void algo(Self* self, Callback callback) {
    // code using self instead of this
    using int_t = as_const_as_t<Self, int>; // const or not depending on self
  }
public:
  template<class Callback>
  void algo(Callback callback) {
    algo( this, callback );
  }
  template<class Callback>
  void algo(Callback callback) const {
    algo( this, callback );
  }

现在我们有两个外部方法algo,一个static方法将自己的类型作为模板类型,可以是const也可以不是。