【问题标题】:const_casting element type of container容器的 const_casting 元素类型
【发布时间】:2015-03-18 18:05:07
【问题描述】:

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

【问题讨论】:

  • 我明白为什么你可能想将std::vector&lt;Point*&gt; 转换为std::vector&lt;const Point*&gt;,但反过来看起来就很狡猾。如果您只是要忽略它,那么 const 有什么意义?
  • 我可以想象有合法的用例,但为了提出解决方案,我必须看看 OP 真正想要做什么。
  • @NeilKirk 即使这样也是有问题的,就好像你将 const Point* 添加到强制转换向量中一样,你已经违反了常量正确性,因为现在你在原始向量中有一个 Point*。这是可变的正方形不是矩形问题。如果 OP 将其向后,则可以创建向量内容的 const 正确 view
  • @tmlen 所以你真正的问题是如何在创建同一函数的 const 和非 const 版本时避免重复代码?
  • @NeilKirk 它是point * 数据结构中的const point*。您将const point* 添加到std::vector&lt; point * &gt;&amp;std::vector&lt; const point *&gt;&amp; 演员表中,它非法到达std::vector&lt; point* &gt;

标签: c++ c++11 stl const-correctness const-cast


【解决方案1】:
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,也可以不是。

【讨论】:

  • 我们确实需要一种更好的方法来模板化 *this 的 type/cv-qualifiers/value 类别的成员函数,而不是转发到静态成员模板。
  • @Casey 什么,3*2*2 对你来说手动重载太多了?我真的应该通过转发&amp;&amp; 引用来获取Self,以便将const&amp;const&amp;&amp; 区分开来,正如您所提到的。它让as_const_as_t 更烦人。
  • 我曾经用常量转移来指出这种可能性。但我认为现在将Self 类型作为模板参数是不好的,而且通常最好将其明确化。因此,在我之前发布的答案中显示了更高级的:)(在更高版本的意义上,进化)。
【解决方案2】:

除了std::vector 是一个标准库类之外,vector&lt;Foo*&gt;vector&lt;Foo const*&gt; 类型可能具有不同的特化和不同的大小。因此,虽然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 ) {}
};

【讨论】:

  • 为了强制执行可变性(或者编译错误),可以使用std::remove_const,我认为它被调用了。但我把它留给读者。是否这样做是一个工程直觉决定。
猜你喜欢
  • 1970-01-01
  • 2010-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
  • 2013-05-06
相关资源
最近更新 更多