【问题标题】:Does this const initialization through const_cast have undefined behaviour?通过 const_cast 进行的 const 初始化是否具有未定义的行为?
【发布时间】:2011-05-16 12:43:18
【问题描述】:

根据我的小测试,此代码有效。但是,它有未定义的行为吗?在我之前的测试中,通过使用 const_cast 修改 const 对象导致了运行时访问冲突,但我不记得它们有何不同。那么,这里是否存在根本性的问题?

// test.h
#pragma once
#include <boost/array.hpp>

typedef boost::array<int,100000> bigLut_t;
extern const bigLut_t constBigLut;

// test.cpp
#include "test.h"

bigLut_t& initializeConstBigLut()
{
    bigLut_t* pBigLut = const_cast<bigLut_t*>( &constBigLut );

    for(int i = 0; i < 100000; ++i) {
        pBigLut->at(i) = i;
    }
    return const_cast<bigLut_t&>(constBigLut);
}

const bigLut_t constBigLut = initializeConstBigLut();

// const_test.cpp
#include <iostream>
#include "test.h"

void main()
{
    for(int i = 0; i < 100; ++i) {
        std::cout << constBigLut[i] << std::endl;
    }
    system("pause");
}

(请注意 sizeof(bigLut_t) 太大而无法放入堆栈。)

编辑: 实际上,我最喜欢 ybungalobill 的小评论中的想法,最适合初始化这些大对象的方法:

// test.h
#pragma once
#include <boost/array.hpp>

extern const struct BigLut : public boost::array<int,100000> {
    BigLut();
} constBigLut;

// test.cpp
#include "test.h"

const BigLut constBigLut;
BigLut::BigLut()
{
    for(int i = 0; i < 100000; ++i) {
        this->at(i) = i;
    }
}

【问题讨论】:

  • 除此之外,void main 在 C++ 中是非法的。 main 必须始终具有返回类型 int。不过,您可以放心地省略 return 语句。

标签: c++ initialization undefined undefined-behavior const-cast


【解决方案1】:

您修改了定义为 const 的对象。不管你什么时候做,在初始化期间与否,它仍然是未定义的行为。仅当 const 指针是在某个较早阶段从指向该对象的非 const 指针获得时,才定义使用 const_cast 删除 const 性。那不是你的情况。

你能做的最好的事情是

const bigLut_t& initializeConstBigLut()
{
    static bigLut_t bigLot;

    for(int i = 0; i < 100000; ++i) {
        bigLut.at(i) = i;
    }
    return bigLut;
}

const bigLut_t constBigLut = initializeConstBigLut();

希望编译器能优化掉静态临时变量。

【讨论】:

    【解决方案2】:

    不幸的是,您正在滥用 const_cast 运算符,这很不幸,并且在这种情况下会生成未定义的行为...您可以通过调用其隐式复制构造函数来为 constBigLut 使用动态初始化程序(假设 boost::arraystd::array):

    struct bigLut_tinit  {  
      bigLut_t BigLut; 
    
      bigLut_tinit() {
        for(int i = 0; i < 100000; ++i) {  
            BigLut[i] = i;  
        }
      }
    };
    
    const bigLut_tinit constBigLut;
    

    编辑:似乎 VC++10 完美地应用了 RVO,以便将临时对象直接移动到静态持续时间对象中。所以恕我直言,无需声明本地静态变量或对临时变量的引用......

    编辑 2:是的,我错过了尺寸问题。建议使用上述构造函数包装成一个非平凡的类型......

    【讨论】:

    • OP 明确表示“sizeof(bigLut_t) 太大,无法放入堆栈。”
    • +1 表示结构的想法。其实你可以做得更好:让结构派生自 bigLut_t,然后使用 constBigLut 的代码将被取消。
    • 其实我根本不会做这样的事情......静态持续时间的对象应该只是一个绝对例外的紧急解决方案。派生形式std::array 会使它更“黑客”恕我直言,因为它显然违反了继承的目的。如果需要这样的事情,您可能会遇到某种架构问题,应该首先解决。如果这不起作用,请将类似 bigLut_tinit 的实例包装到一个安全的单例中,这样它就不会被进一步滥用......
    • @paul_71:为了让我的应用程序正常工作,那些全局 const 对象总是需要存在,所以我看不出有任何理由延迟它们的初始化。此外, constBigLut 的基类在概念上不是单例(也不一定是 std::array )。这意味着我确实需要(在代码中的其他地方)构造和破坏与某些全局 const 对象相同类型的对象。除了将静态持续时间的对象视为架构问题之外,您能否给我一些其他理由来解释为什么静态持续时间的对象应该只是一种特殊的紧急解决方案?
    • 这还不够吗? :) 不,C++ 静态有几个固有的问题。
    【解决方案3】:

    它是一个UB,因为该数组可以存储在ROM中。

    你可以这样做:

    // test.h
    #include <boost/array.hpp>
    
    typedef boost::array<int,100000> bigLut_t;
    const bigLut_t& Lut();
    
    
    // test.cpp
    #include "test.h"
    
    bool initialized=false;
    
    const bigLut_t& Lut()
    {
      static bigLut_t lut;
    
      if (!initialized)
      {
        for(int i = 0; i < 100000; ++i) {
            lut.at(i) = i;
        }
      }
        return lut;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-13
      • 2011-08-23
      • 1970-01-01
      • 2021-08-30
      • 2015-02-28
      • 1970-01-01
      相关资源
      最近更新 更多