2010-11-27 19 views
7

Nach meinen kleinen Tests funktioniert dieser Code. Aber, hat es undefiniertes Verhalten? Das Ändern des const-Objekts durch Verwendung von const_cast führte zu Laufzeitzugriffsverletzungen in meinen vorherigen Tests, aber ich kann mich nicht erinnern, wie unterschiedlich sie waren. Also, ist hier grundsätzlich etwas falsch oder nicht?Hat diese Const-Initialisierung durch const_cast ein undefiniertes Verhalten?

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

(Beachten Sie, dass sizeof (bigLut_t) ist zu viel in den Stapel.)

EDIT: Eigentlich mag ich die Idee in ybungalobill des Kleiner Kommentar am besten für eine Methode zur Initialisierung dieser großen Objekte:

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

abgesehen von dem Rest, 'Leere main' ist illegal in C++. 'main' muss ** immer ** den Rückgabetyp' int' haben. Sie können die Anweisung 'return' jedoch sicher weglassen. –

Antwort

6

Sie ein Objekt als const definiert ändern. Es spielt keine Rolle, wann Sie es tun, während der Initialisierung oder nicht, es ist immer noch undefiniertes Verhalten. Das Entfernen von constness mit const_cast ist nur definiert, wenn der const-Zeiger zu einem früheren Zeitpunkt von einem nichtkonstanten Zeiger auf dieses Objekt erhalten wurde. Das ist nicht dein Fall.

Das Beste, was Sie tun können, ist

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

und hoffentlich wird der Compiler die statische temporäre Optimierung aus.

3

Sie mißbrauchen die const_cast Betreiber, die leider möglich ist, und in diesem Fall nicht definiertes Verhalten erzeugt ... können Sie dynamische Initialisierer für constBigLut verwenden, indem Sie die implizite Copykonstruktor Aufruf (unter der Annahme, dass boost::array das gleiche Konzept wie std::array ist):

struct bigLut_tinit { 
    bigLut_t BigLut; 

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

const bigLut_tinit constBigLut; 

Edit: es scheint, dass VC++ 10 perfekt RVO gilt, so dass die temporäre direkt in die statische Dauer Objekt bewegt wird. Also imho keine Notwendigkeit, lokale Statik oder Verweise auf Tempraries zu deklarieren ...

Edit 2: Ja, ich habe die Größe Problem verpasst. Empfehlen Sie das Wrapping in einen nicht-trivialen Typ mit Konstruktor wie oben ...

+0

Das OP sagte explizit, dass "sizeof (bigLut_t) zu viel ist, um in den Stapel zu passen." – ybungalobill

+1

+1 für die Struktur Idee. Eigentlich können Sie es noch besser machen: Lassen Sie die Struktur von bigLut_t ableiten, und dann wird der Code, der constBigLut verwendet, nicht gechattet. – ybungalobill

+0

Eigentlich würde ich sowas gar nicht machen ... Objekte von statischer Dauer sollten nur eine absolut außergewöhnliche Notlösung sein. Das Ableiten der Form "std :: array" würde das sogar "hackier" machen, da es eindeutig den Zweck der Vererbung verletzen würde. Wenn so etwas notwendig wird, haben Sie wahrscheinlich ein architektonisches Problem, das zuerst gelöst werden sollte.Wenn das nicht klappt, wickle etwas wie "bigLut_tinit" -Instanz in ein sicheres Singleton, damit es nicht weiter missbraucht werden kann ... –

1

Es ist ein UB, weil dieses Array im ROM gespeichert werden könnte.

Sie können dies tun:

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