Fa questa inizializzazione const attraverso const_cast hanno un comportamento indefinito?
-
28-09-2019 - |
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;
}
}
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;
}