【问题标题】:Modifying element of const std::vector<T> via const_cast通过 const_cast 修改 const std::vector<T> 的元素
【发布时间】:2019-09-22 11:31:52
【问题描述】:

以下程序是否有未定义的行为?

#include <iostream>
#include <vector>

struct Foo
{
    const std::vector<int> x;
};

int main()
{
    std::vector<int> v = {1,2,3};
    auto f = new Foo{v};
    const_cast<int&>(f->x[1]) = 42; // Legal?
    std::cout << f->x[1] << "\n";
}

请注意,它不是使用const_castf-&gt;x 中剥离常量,而是从f-&gt;x[x] 中剥离常量,这可能由一个单独的数组表示。还是允许翻译假定f-&gt;x[1] 在创建后是不可变的?

【问题讨论】:

  • 结构没有区别,问题与int main() { const std::vector&lt;int&gt; y(1); (int&amp;)y[0] = 42; }相同
  • 如果您编写自己的类是 std::vector 的精确克隆,我非常有信心该代码是合法的。有时该标准有额外的条款说标准库容器具有特殊属性(例如,不完整类型的 std::vector 导致 UB);但是在这种情况下我找不到任何相关的东西。
  • 如果你有一个库分发const std::vector&lt;int&gt; &amp;s,那么请注意库没有假设ints 不会改变,即使编译器不能假设。

标签: c++ stl language-lawyer stdvector const-cast


【解决方案1】:

您的示例中没有未定义的行为。

上面的代码不会调用未定义的行为,因为底层数据 (int) 是可变的。让我们看一个更简单的例子。

#include <iostream>

struct IntPtr {
    int* data;
};

int main() {
    int a = 0;
    const IntPtr p { &a }; 
    *p.data = 10;
    std::cout << a; // Prints 10
}

所有这一切都是完全合法的,因为使IntPtr const 导致data 是一个指向int 的常量指针,而不是一个指向常量int 的指针。我们可以修改p.data指向的数据;我们只是不能修改p.data 本身。

const IntPtr p { &a };

*p.data = 10; // This is legal

int b;
p.data = &b; // This is illegal (can't modify const member)

那么这个例子如何适用于std::vector 让我们添加索引到IntPtr 的功能:

class IntPtr {
    int* data;
   public:
    IntPtr() = default;
    IntPtr(int size) : data(new int[size]) {}
    
    // Even though this is a const function we can return a mutable reference 
    // because the stuff data points to is still mutable. 
    int& operator[](int index) const {
        return data[index]; 
    }
};

int main() {
    const IntPtr i(100); 
    i[1] = 10; // This is fine
};

即使IntPtr 是常量,底层数据也是可变的,因为它是通过分配一组可变整数来创建的。 std::vector 也一样:底层数据仍然是可变的,所以 const_cast 它是安全的。

【讨论】:

    猜你喜欢
    • 2021-06-02
    • 1970-01-01
    • 2019-10-20
    • 1970-01-01
    • 2012-07-14
    • 2011-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多