수색…
비고
구문의 동작이 지정되지 않은 경우 표준은 동작에 대한 몇 가지 제약 조건을 적용하지만 구현시 약간의 자유를 남깁니다. 이는 주어진 상황에서 일어나는 일을 문서화 하지 않아도됩니다. 이것은 구현이 정의 된 동작 과 대조적으로, 구현 은 일어나는 일을 문서화해야하며 정의되지 않은 동작은 어떤 일이 발생할 수 있습니다.
TU에서 전역의 초기화 순서
Translation Unit 내부에서는 전역 변수의 초기화 순서가 지정되어 있지만 Translation Units에 대한 초기화 순서는 지정되지 않습니다.
따라서 다음 파일이있는 프로그램
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의 값
범위가 지정된 enum이 값을 보유하기에 너무 작은 정수 유형으로 변환되면 결과 값은 지정되지 않습니다. 예:
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 * value의 정적 캐스트
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);
p2
는 int
형의 객체를 가리킬 수 없기 때문에 p3
의 값은 지정되지 않습니다. 그 값은 올바르게 정렬 된 주소가 아닙니다.
일부 reinterpret_cast 변환 결과
하나의 함수 포인터 유형에서 다른 함수 유형으로의 reinterpret_cast
또는 하나의 함수 참조 유형에서의 결과는 지정되지 않습니다. 예:
int f();
auto fp = reinterpret_cast<int(*)(int)>(&f); // fp has unspecified value
하나의 오브젝트 포인터 유형에서 다른 오브젝트 유형으로의 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에서는 함수 인수에 대한 평가 순서가 아직 정해지지 않았습니다.
그러나 각 함수 인수는 완전히 평가되고 호출 인수는 함수 인수가 오기 전에 평가됩니다.
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)
make
또는 from
의 후 bar
를 출력 하지 않을 수 있으며, 인쇄되지 않을 수 있습니다 :
bar
make_int(2)
make_int(1)
from_int(2)
from_int(1)
또는 유사합니다. make_int
가 유효한 후에 C ++ 17 인쇄 bar
이전에, from_int
를 수행하기 전에 make_int
수행하는 것과 마찬가지로.
가장 표준적인 라이브러리 클래스의 이동 된 상태
모든 표준 라이브러리 컨테이너는 이동 한 후 유효하지만 지정되지 않은 상태로 남습니다. 예를 들어 다음 코드에서 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이됩니다.