Domanda

Secondo le mie piccole prove questo codice funziona. Ma, ce l'ha un comportamento indefinito? Modifica l'oggetto const attraverso l'uso di const_cast comportato violazioni di accesso in fase di esecuzione nei miei test precedenti, ma non riesco a ricordare il modo in cui erano diverse. Quindi, c'è fondamentalmente qualcosa di sbagliato qui o no?

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

(Si noti che sizeof (bigLut_t) è troppo per adattarsi nello stack.)

EDIT: Io in realtà come l'idea nel piccolo commento di ybungalobill migliore per un metodo di inizializzazione questi grandi oggetti:

// 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;
    }
}
È stato utile?

Soluzione

È modificare un oggetto definito come const. Non importa quando lo si fa, durante l'inizializzazione o no, è ancora un comportamento indefinito. Rimozione constness con const_cast è definito solo se il puntatore const è stato ottenuto da un puntatore non-const a quell'oggetto in qualche fase precedente. Questo non è il vostro caso.

La cosa migliore che puoi fare è

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

e speriamo che il compilatore di ottimizzare la statica temporanea.

Altri suggerimenti

Stai abusando l'operatore const_cast che purtroppo è possibile, e in questo caso genera un comportamento indefinito ... È possibile utilizzare inizializzatore dinamica per constBigLut facendo valere il proprio costruttore di copia implicita (assumendo che boost::array è lo stesso concetto come 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: Sembra che VC ++ 10 applica perfettamente RVO, in modo che il temporaneo è direttamente spostato nell'oggetto durata statico. Così IMHO non c'è bisogno di dichiarare statica locali o riferimenti a tempraries ...

Modifica 2: Sì, ho perso il problema delle dimensioni. Consiglia involucro in un tipo non banale con il costruttore come sopra ...

È un UB, perché tale matrice potrebbe essere memorizzato nella ROM.

Si potrebbe fare questo:

// 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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top