Sök…


Anmärkningar

Om beteendet hos en konstruktion är ospecificerad lägger standarden vissa begränsningar för beteendet, men lämnar viss frihet till implementeringen, vilket inte krävs för att dokumentera vad som händer i en given situation. Det kontrasterar med genomförandet definierade beteende , där genomförandet krävs för att dokumentera vad som händer, och odefinierade beteende, där allt kan hända.

Initieringsordning för globaler över TU

Medan inuti en översättningsenhet specificeras ordningen för initialisering av globala variabler, specificeras ordningen för initialisering över översättningsenheter.

Så programmera med följande filer

  • 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 producera som utgång:

foobar

eller

barfoo

Det kan leda till statisk initialiseringsorder Fiasco .

Värdet på en out-of-range enum

Om en scoped enum konverteras till en integrerad typ som är för liten för att hålla dess värde, är det resulterande värdet ospecificerat. Exempel:

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

Om ett heltal konverteras till ett enum och heltalets värde ligger utanför området för enumvärdena, är det resulterande värdet ospecificerat. Exempel:

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

Men i nästa exempel är beteendet inte ospecificerad, eftersom källvärdet är inom räckhåll för enum, även om det är olika för alla enumerators:

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

Här s kommer att ha värdet tre och vara olika till ONE , TWO och FOUR .

Statisk gjutning från falskt tomrum * -värde

Om ett void* -värde konverteras till en pekare till objekttyp, T* , men inte är korrekt justerat för T , är det resulterande pekarvärdet ospecificerat. Exempel:

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

Värdet på p3 är inte specificerat eftersom p2 inte kan peka på ett objekt av typen int ; dess värde är inte en korrekt justerad adress.

Resultat av några omintolkade_cast-omvandlingar

Resultatet av en reinterpret_cast från en funktionspekartyp till en annan, eller en funktionsreferenstyp till en annan, är inte specificerad. Exempel:

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

Resultatet av en reinterpret_cast från en objektpekartyp till en annan, eller en objektreferenstyp till en annan, är inte specificerad. Exempel:

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

Men för de flesta kompilatorer var detta ekvivalent med static_cast<char*>(static_cast<void*>(&x)) så den resulterande pekaren p pekade på den första byten av x . Detta gjordes som standardbeteende i C ++ 11. Se typ punningkonvertering för mer information.

Resultat av vissa pekarsjämförelser

Om två pekare jämförs med < , > , <= eller >= , är resultatet ospecificerat i följande fall:

  • Pekarna pekar på olika matriser. (Ett objekt utan array betraktas som en matris med storlek 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
    
  • Pekarna pekar på samma objekt, men till medlemmar med olika åtkomstkontroll.

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

Utrymme ockuperat av en referens

En referens är inte ett objekt, och till skillnad från ett objekt är det inte garanterat att uppta vissa sammanhängande byte av minne. Standarden lämnar det ospecificerat om en referens kräver någon lagring alls. Ett antal funktioner i språket konspirerar för att göra det omöjligt att portabelt undersöka lagring som referensen kan uppta:

  • Om sizeof tillämpas på en referens, returnerar den storleken på den refererade typen och ger därmed ingen information om referensen upptar någon lagring.
  • Referenser är olagliga, så det är inte möjligt att undersöka adresserna för två på varandra följande element i en hypotetisk referens av matriser för att bestämma storleken på en referens.
  • Om adressen till en referens tas är resultatet referensens adress, så vi kan inte få en pekare till själva referensen.
  • Om en klass har en referensmedlem ger försök att extrahera adressen till den medlemmen med offsetof odefinierat beteende eftersom en sådan klass inte är en standardlayoutklass.
  • Om en klass har en referensmedlem är klassen inte längre standardlayout, så försöker få åtkomst till data som används för att lagra referensresultaten i odefinierat eller ospecificerat beteende.

I praktiken kan i vissa fall en referensvariabel implementeras på liknande sätt som en pekvariabel och därmed uppta samma mängd lagring som en pekare, medan i andra fall en referens inte kan uppta något utrymme alls eftersom det kan optimeras. Till exempel i:

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

kompilatorn är gratis att helt enkelt behandla r som ett alias för x och ersätta alla förekomster av r i resten av funktionen f med x , och inte tilldela någon lagring för att hålla r .

Utvärderingsordning för funktionsargument

Om en funktion har flera argument är det ospecificerat vilken ordning de utvärderas i. Följande kod kan skriva ut x = 1, y = 2 eller x = 2, y = 1 men det är ospecificerat vilket.

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

I C ++ 17 förblir orden på utvärdering av funktionsargument ospecificerad.

Men varje funktionsargument utvärderas fullständigt, och det anropande objektet garanteras utvärderas innan det finns några funktionsargument.

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

detta måste skriva ut:

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

eller

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

det får inte skriva ut bar efter något av make eller from , och det får inte skriva ut:

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

eller liknande. Före C ++ 17 tryckning bar efter make_int s var lagligt, som gjorde både make_int s före att göra någon from_int s.

Flyttad från tillståndet för de flesta standardbibliotekskurser

C ++ 11

Alla standardbiblioteksbehållare lämnas i ett giltigt men ospecificerat tillstånd efter att de har flyttats från. Till exempel, i följande kod kommer v2 att innehålla {1, 2, 3, 4} efter flytten, men v1 är inte garanterat att vara tom.

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

Vissa klasser har ett exakt definierat flyttat tillstånd. Det viktigaste fallet är det av std::unique_ptr<T> , som garanteras att vara noll efter att ha flyttats från.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow