7

내 작은 테스트에 따르면이 코드가 작동합니다. 그러나 그것은 정의되지 않은 행동을합니까? const_cast를 사용하여 const 개체를 수정하면 이전 테스트에서 런타임 액세스 위반이 발생했지만 어떻게 다른지 기억할 수 없습니다. 근본적으로 무언가 잘못 되었나요? 아닙니다.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)를 주목 스택에 맞게 너무 많이.)

편집 : I 실제로 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; 
    } 
} 
+6

을 제외하고 나머지,'무효 main'는 C에서 불법 ++에서. 'main'은 항상 ** int ** 타입의 리턴 값을 가져야합니다. 그러나'return' 문은 안전하게 생략 할 수 있습니다. –

답변

6

당신은 const를 정의 객체를 수정합니다. 그것은 당신이 그것을 할 때, 초기화 도중 또는 중요하지 않습니다, 그것은 여전히 ​​정의되지 않은 동작입니다. const_cast를 사용하여 const_cast를 제거하는 것은 const 포인터가 초기 단계에서 해당 객체에 대한 비 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(); 

과 희망 컴파일러는 정적 일시적 밖으로 최적화합니다.

3

당신은 불행하게도 가능하다에 const_cast 연산자 오용되고,이 경우는 정의되지 않은 동작이 발생에 ... 당신은 암시 복사 생성자 (boost::arraystd::array과 동일한 개념 인 것으로 가정)를 호출하여 constBigLut 동적 초기화를 사용할 수있다 :

struct bigLut_tinit { 
    bigLut_t BigLut; 

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

const bigLut_tinit constBigLut; 

편집 : 임시 직접 정지 계속 객체로 이동되도록 VC++ (10)가 완벽하게, 망막 정맥 폐쇄를 적용 보인다. 그래서 imho는 지방 통계 또는 tempraries에 대한 언급을 선언 할 필요가 없습니다 ...

편집 2 : 그래, 크기 문제를 놓쳤습니다. 위와 같이 생성자를 사용하여 중요하지 않은 형식으로 래핑하는 것이 좋습니다 ...

+0

OP는 명시 적으로 "sizeof (bigLut_t)가 스택에 너무 적합합니다."라고 말했습니다. – ybungalobill

+1

+1 구조체 아이디어입니다. 실제로 struct를 bigLut_t에서 파생 시키면 constBigLut을 사용하는 코드가 unchaged됩니다. – ybungalobill

+0

사실 나는 그와 같은 것을 전혀하지 않을 것입니다 ... 정적 지속 기간의 객체는 절대적으로 예외적 인 비상 솔루션이어야합니다. 형식'std :: array'를 파생 시키면 상속의 목적을 분명히 위반했기 때문에 심지어 'hackier'IMHO가됩니다. 이와 같은 것이 필요한 경우, 아마도 먼저 해결해야 할 아키텍처 문제가있을 것입니다.그게 해결되지 않으면'bigLut_tinit' 인스턴스를 안전 싱글 톤으로 랩핑하여 더 이상 악용 될 수 없도록 ... –

1

이 배열은 ROM에 저장 될 수 있으므로 UB입니다.

이 작업을 수행 할 수 있습니다 :

// 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; 
}