Поиск…


замечания

Если поведение конструкции не указано, то стандарт помещает некоторые ограничения на поведение, но оставляет некоторую свободу для реализации, что не требуется для документирования того, что происходит в данной ситуации. Это контрастирует с реализацией определяется поведением , в которых требуется осуществление документировать то , что происходит, и непредсказуемое поведение, в котором все может случиться.

Порядок инициализации globals через TU

Если внутри единицы перевода указывается порядок инициализации глобальных переменных, порядок инициализации между единицами перевода не указан.

Итак, программа со следующими файлами

  • foo.cpp

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

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

    int main() {}
    

может вырабатываться как результат:

foobar

или же

barfoo

Это может привести к статическому порядку инициализации Fiasco .

Значение перераспределения вне диапазона

Если облачное перечисление преобразуется в интегральный тип, который слишком мал, чтобы удерживать его значение, результирующее значение не указывается. Пример:

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

Кроме того, если целое число преобразуется в перечисление, а значение целочисленного значения выходит за пределы значений перечисления, результирующее значение не указывается. Пример:

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

Однако в следующем примере поведение не является неопределенным, так как исходное значение находится в пределах диапазона перечисления, хотя оно неравномерно для всех счетчиков:

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

Здесь s будет иметь значение 3 и быть неравным с ONE , TWO и FOUR .

Статический литье из фиктивного значения void *

Если значение void* преобразуется в указатель на тип объекта, T* , но не правильно выровнено для T , результирующее значение указателя не указывается. Пример:

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

Значение p3 не определено, поскольку p2 не может указывать на объект типа int ; его значение не является правильно выровненным адресом.

Результат некоторых преобразований reinterpret_cast

Результат reinterpret_cast из одного типа указателя функции в другой или один тип ссылочного типа в другой не указан. Пример:

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

Результат reinterpret_cast от одного типа указателя объекта к другому, или один тип ссылки объекта к другому, не указан. Пример:

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

Однако с большинством компиляторов это было равнозначно static_cast<char*>(static_cast<void*>(&x)) поэтому полученный указатель p указывал на первый байт x . Это стало стандартным поведением в C ++ 11. Подробнее см. В разделе « Преобразование чисел» .

Результат некоторых сравнений указателей

Если два указателя сравниваются с использованием < , > , <= , или >= , результат не указывается в следующих случаях:

  • Указатели указывают на разные массивы. (Объект без массива считается массивом размера 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
    
  • Указатели указывают на один и тот же объект, но на участников с различным контролем доступа.

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

Пространство, занятое ссылкой

Ссылка не является объектом, и, в отличие от объекта, не гарантируется, что он занимает несколько смежных байтов памяти. Стандарт оставляет его неопределенным, если ссылка требует какого-либо хранения вообще. Ряд особенностей языкового сговора, чтобы сделать невозможным портативное исследование любого хранилища, которое может занимать эта ссылка:

  • Если sizeof применяется к ссылке, он возвращает размер ссылочного типа, тем самым не давая информации о том, занимает ли эта ссылка какое-либо хранилище.
  • Массивы ссылок являются незаконными, поэтому невозможно определить адреса двух последовательных элементов гипотетической ссылки массивов, чтобы определить размер ссылки.
  • Если адрес ссылки сделан, результатом будет адрес референта, поэтому мы не сможем получить указатель на саму ссылку.
  • Если у класса есть ссылочный элемент, попытка извлечь адрес этого члена с помощью offsetof дает неопределенное поведение, поскольку такой класс не является стандартным классом макета.
  • Если класс имеет ссылочный элемент, класс больше не является стандартным макетом, поэтому попытки доступа к любым данным, используемым для хранения ссылочных результатов, в неопределенном или неуказанном поведении.

На практике в некоторых случаях эталонная переменная может быть реализована аналогично переменной указателя и, следовательно, занимает ту же сумму хранения, что и указатель, в то время как в других случаях эта ссылка может вообще не занимать места, поскольку ее можно оптимизировать. Например, в:

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

компилятор может просто рассматривать r как псевдоним для x и заменять все вхождения r в остальную часть функции f на x и не выделять какое-либо хранилище для хранения r .

Порядок оценки аргументов функции

Если функция имеет несколько аргументов, то неуказано, в какой порядок они оцениваются. Следующий код может печатать x = 1, y = 2 или x = 2, y = 1 но не указано, что.

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

В C ++ 17 порядок оценки аргументов функции остается неопределенным.

Тем не менее, каждый аргумент функции полностью оценивается, и вызывающий объект гарантированно оценивается перед любыми аргументами функции.

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

это должно печатать:

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

или же

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

он не может печатать bar после любого из make или from 's, и он не может печатать:

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

или похожие. До bar C ++ 17 после make_int s было законным, так как делало оба make_int перед тем, как делать какие-либо from_int s.

Перемещение из состояния большинства стандартных классов библиотеки

C ++ 11

Все контейнеры стандартной библиотеки остаются в правильном, но неуказанном состоянии после перемещения из. Например, в следующем коде v2 будет содержать {1, 2, 3, 4} после перемещения, но v1 не гарантированно будет пустым.

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

Некоторые классы имеют точно определенное перемещенное состояние. Самый важный случай - это std::unique_ptr<T> , который после переноса будет иметь значение null.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow