2010-11-27 32 views
7

Selon mes petits tests ce code fonctionne. Mais, a-t-il un comportement indéfini? La modification de l'objet const à l'aide de const_cast a entraîné des violations d'accès au moment de l'exécution lors de mes tests précédents, mais je ne me rappelle plus en quoi elles étaient différentes. Donc, y a-t-il fondamentalement quelque chose de mal ici ou pas?Cette initialisation const via const_cast a-t-elle un comportement indéfini?

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

(Notez que sizeof (bigLut_t) est trop pour entrer dans la pile.)

EDIT: fait je aime l'idée dans les années ybungalobill petit commentaire le mieux pour une méthode d'initialisation de ces gros objets:

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

En dehors du reste, 'main' vide est illégale en C++. 'main' doit toujours avoir le type de retour' int'. Vous pouvez cependant omettre l'instruction 'return' en toute sécurité. –

Répondre

6

Vous modifier un objet défini comme const. Peu importe quand vous le faites, lors de l'initialisation ou non, c'est toujours un comportement indéfini. La suppression de const avec const_cast n'est définie que si le pointeur const a été obtenu à un stade antérieur à partir d'un pointeur non-const sur cet objet. Ce n'est pas ton cas.

La meilleure chose que vous pouvez faire est

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(); 

et nous espérons que le compilateur d'optimiser le temporaire statique.

3

Vous utilisent à mauvais escient l'opérateur const_cast qui est malheureusement possible, et dans ce cas génère un comportement non défini ... Vous pouvez utiliser initialiseur dynamique pour constBigLut en invoquant son constructeur de copie implicite (en supposant que boost::array est le même concept que std::array):

struct bigLut_tinit { 
    bigLut_t BigLut; 

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

const bigLut_tinit constBigLut; 

Edit: Il semble que VC++ 10 applique parfaitement RVO, de sorte que le temporaire est déplacé directement dans l'objet de durée statique. Donc, je n'ai pas besoin de déclarer des statistiques locales ou des références à tempraries ...

Édition 2: Oui, j'ai raté le problème de taille. Recommander wrapping dans un type non trivial avec le constructeur comme ci-dessus ...

+0

L'OP a explicitement déclaré que "sizeof (bigLut_t) est trop grand pour tenir dans la pile." – ybungalobill

+1

+1 pour l'idée de structure. En fait, vous pouvez le rendre encore meilleur: faites dériver la structure de bigLut_t, puis le code utilisant constBigLut sera déchaîné. – ybungalobill

+0

En fait, je ne ferais pas quelque chose comme ça ... les objets de durée statique ne devraient être qu'une solution d'urgence absolument exceptionnelle. Dériver le formulaire 'std :: array' rendrait cela encore plus« hackier »à mon humble avis, car cela violerait clairement le but de l'héritage. Si quelque chose de ce genre est nécessaire, vous aurez probablement un problème d'architecture qui devrait être résolu en premier.Si cela ne fonctionne pas, placez quelque chose comme l'instance 'bigLut_tinit' dans un singleton sécurisé, afin qu'il ne puisse plus être utilisé ... –

1

Il s'agit d'un UB, car ce tableau pourrait être stocké dans la ROM.

Vous pouvez le faire:

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