容器的const_cast元素类型

const_casting element type of container

本文关键字:cast 元素 类型 const      更新时间:2023-10-16

是否有一种有效且安全的方法将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_cast<A&>(*this).algo()。但这似乎更危险,因为A对象可能被创建为const(const A a;),然后它就是UB。

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也可以不是。

除了std::vector是一个标准库类之外,类型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;

然后你可以编写这样的类:

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 ) {}
};