Zoeken…


Opmerkingen

Als het gedrag van een construct niet is gespecificeerd, legt de standaard enkele beperkingen aan het gedrag, maar laat de implementatie wat vrijheid over, wat niet vereist is om te documenteren wat er in een bepaalde situatie gebeurt. Het staat in contrast met door de implementatie gedefinieerd gedrag , waarbij de implementatie vereist is om te documenteren wat er gebeurt, en ongedefinieerd gedrag, waarin alles kan gebeuren.

Volgorde van initialisatie van globals in TU

Terwijl binnen een vertaaleenheid de volgorde van initialisatie van globale variabelen is gespecificeerd, is de volgorde van initialisatie over vertaaleenheden niet gespecificeerd.

Dus programma met volgende bestanden

  • foo.cpp

    #include <iostream>
    
    int dummyFoo = ((std::cout << "foo"), 0);
    
  • bar.cpp

    #include <iostream>
    
    int dummyBar = ((std::cout << "bar"), 0);
    
  • main.cpp

    int main() {}
    

kan als output produceren:

foobar

of

barfoo

Dat kan leiden tot een statische initialisatieorder Fiasco .

Waarde van een opsomming buiten bereik

Als een scopedum wordt geconverteerd naar een integraal type dat te klein is om zijn waarde te behouden, is de resulterende waarde niet gespecificeerd. Voorbeeld:

enum class E {
    X = 1,
    Y = 1000,
};
// assume 1000 does not fit into a char
char c1 = static_cast<char>(E::X); // c1 is 1
char c2 = static_cast<char>(E::Y); // c2 has an unspecified value

Als een geheel getal wordt geconverteerd naar een opsomming en de waarde van de gehele getallen buiten het bereik van de waarden van de opsomming valt, is de resulterende waarde niet gespecificeerd. Voorbeeld:

enum Color {
    RED = 1,
    GREEN = 2,
    BLUE = 3,
};
Color c = static_cast<Color>(4);

In het volgende voorbeeld is het gedrag echter niet gespecificeerd, omdat de bronwaarde binnen het bereik van het opsomming valt, hoewel het ongelijk is voor alle opsommers:

enum Scale {
    ONE = 1,
    TWO = 2,
    FOUR = 4,
};
Scale s = static_cast<Scale>(3);

Hier zal s de waarde 3 hebben en ongelijk zijn aan ONE , TWO en FOUR .

Statische cast van nepwaarde *

Als een waarde voor void* wordt geconverteerd naar een pointer naar objecttype, T* , maar niet correct is uitgelijnd voor T , is de resulterende pointerwaarde niet gespecificeerd. Voorbeeld:

// Suppose that alignof(int) is 4
int x = 42;
void* p1 = &x;
// Do some pointer arithmetic...
void* p2 = static_cast<char*>(p1) + 2;
int* p3 = static_cast<int*>(p2);

De waarde van p3 is niet gespecificeerd omdat p2 niet naar een object van het type int kan wijzen; de waarde is geen correct uitgelijnd adres.

Resultaat van enkele reinterpret_cast-conversies

Het resultaat van een reinterpret_cast van het ene functiepointertype naar het andere, of het ene functiereferentietype naar het andere, is niet gespecificeerd. Voorbeeld:

int f();
auto fp = reinterpret_cast<int(*)(int)>(&f); // fp has unspecified value
C ++ 03

Het resultaat van een reinterpret_cast van het ene objectpointertype naar het andere, of een objectreferentietype naar een ander, is niet gespecificeerd. Voorbeeld:

int x = 42;
char* p = reinterpret_cast<char*>(&x); // p has unspecified value

Bij de meeste compilers was dit echter gelijk aan static_cast<char*>(static_cast<void*>(&x)) dus de resulterende aanwijzer p wees naar de eerste byte van x . Dit werd het standaardgedrag in C ++ 11. Zie type punning conversie voor meer informatie.

Resultaat van enkele pointervergelijkingen

Als twee pointers worden vergeleken met < , > , <= of >= , is het resultaat niet gespecificeerd in de volgende gevallen:

  • De wijzers wijzen in verschillende arrays. (Een niet-arrayobject wordt beschouwd als een array met grootte 1.)

    int x;
    int y;
    const bool b1 = &x < &y;            // unspecified
    int a[10];
    const bool b2 = &a[0] < &a[1];      // true
    const bool b3 = &a[0] < &x;         // unspecified
    const bool b4 = (a + 9) < (a + 10); // true
                                        // note: a+10 points past the end of the array
    
  • De verwijzingen wijzen naar hetzelfde object, maar naar leden met verschillende toegangscontrole.

    class A {
      public:
        int x;
        int y;
        bool f1() { return &x < &y; } // true; x comes before y
        bool f2() { return &x < &z; } // unspecified
      private:
        int z;
    };
    

Ruimte ingenomen door een referentie

Een referentie is geen object, en in tegenstelling tot een object, is het niet gegarandeerd dat het enkele aaneengesloten bytes geheugen in beslag neemt. De standaard laat het niet gespecificeerd of een referentie überhaupt opslag vereist. Een aantal functies van de taal samenspannen om het onmogelijk te maken om de opslag die de referentie in beslag kan nemen, draagbaar te onderzoeken:

  • Als sizeof op een referentie wordt toegepast, retourneert deze de grootte van het type waarnaar wordt verwezen, waardoor geen informatie wordt gegeven over de vraag of de referentie opslagruimte in beslag neemt.
  • Arrays van referenties zijn illegaal, dus het is niet mogelijk om de adressen van twee opeenvolgende elementen van een hypothetische referentie van arrays te onderzoeken om de grootte van een referentie te bepalen.
  • Als het adres van een referentie wordt overgenomen, is het resultaat het adres van de referent, dus we kunnen geen aanwijzer naar de referentie zelf krijgen.
  • Als een klasse een referentielid heeft, levert een poging om het adres van dat lid te extraheren met behulp van offsetof ongedefinieerd gedrag op omdat een dergelijke klasse geen klasse met standaardopmaak is.
  • Als een klasse een referentielid heeft, is de klasse niet langer standaardindeling, dus pogingen om toegang te krijgen tot gegevens die worden gebruikt om de referentie op te slaan, resulteren in ongedefinieerd of niet-gespecificeerd gedrag.

In de praktijk kan in sommige gevallen een referentievariabele op dezelfde manier worden geïmplementeerd als een pointervariabele en dus dezelfde hoeveelheid opslagruimte innemen als een pointer, terwijl in andere gevallen een referentie helemaal geen ruimte inneemt omdat deze kan worden geoptimaliseerd. Bijvoorbeeld in:

void f() {
    int x;
    int& r = x;
    // do something with r
}

de compiler is vrij om r eenvoudig te behandelen als een alias voor x en alle gevallen van r in de rest van de functie f door x , en geen opslag toe te wijzen aan r .

Evaluatie volgorde van functieargumenten

Als een functie meerdere argumenten heeft, is niet gespecificeerd in welke volgorde ze worden geëvalueerd. De volgende code kan x = 1, y = 2 of x = 2, y = 1 afdrukken x = 2, y = 1 maar het is niet gespecificeerd welke.

int f(int x, int y) {
    printf("x = %d, y = %d\n", x, y);
}
int get_val() {
    static int x = 0;
    return ++x;
}
int main() {
    f(get_val(), get_val());
}
C ++ 17

In C ++ 17 blijft de volgorde van evaluatie van functieargumenten niet gespecificeerd.

Elk functieargument wordt echter volledig geëvalueerd en het aanroepende object wordt gegarandeerd geëvalueerd voordat er functieargumenten zijn.

struct from_int {
  from_int(int x) { std::cout << "from_int (" << x << ")\n"; }
};
int make_int(int x){ std::cout << "make_int (" << x << ")\n"; return x; }


void foo(from_int a, from_int b) {
}
void bar(from_int a, from_int b) {
}

auto which_func(bool b){
  std::cout << b?"foo":"bar" << "\n";
  return b?foo:bar;
}

int main(int argc, char const*const* argv) {
  which_func( true )( make_int(1), make_int(2) );
}

dit moet afdrukken:

bar
make_int(1)
from_int(1)
make_int(2)
from_int(2)

of

bar
make_int(2)
from_int(2)
make_int(1)
from_int(1)

het mag geen bar afdrukken na een van het make of from , en het kan niet afdrukken:

bar
make_int(2)
make_int(1)
from_int(2)
from_int(1)

of vergelijkbaar. Voorafgaand aan de C ++ 17 printing bar na make_int s legaal was, zo deed zowel make_int s voorafgaand aan het doen van een from_int s.

Verplaatst van de status van de meeste standaard bibliotheekklassen

C ++ 11

Alle standaard bibliotheekcontainers worden na verplaatsing in een geldige maar niet-gespecificeerde staat achtergelaten. In de volgende code bevat v2 bijvoorbeeld {1, 2, 3, 4} na de verplaatsing, maar v1 is niet gegarandeerd leeg.

int main() {
    std::vector<int> v1{1, 2, 3, 4};
    std::vector<int> v2 = std::move(v1);
}

Sommige klassen hebben een precies gedefinieerde verplaatst status. Het belangrijkste geval is dat van std::unique_ptr<T> , dat gegarandeerd null is nadat het is verplaatst.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow