태그 보관물: aggregate-initialization

aggregate-initialization

C ++ 0x에서 변환 범위를 좁 힙니다. 그것은 나뿐입니까, 아니면 이것이 큰 변화처럼 들립니까? 경우 코드에서이 문제를 해결하는 데 많은 노력이

C ++ 0X는 소위 필요로하기 때문에, 다음 코드와 유사한 코드가 잘못 형성 할 예정이다 축소 변환 (A)의 doubleA와를 int.

int a[] = { 1.0 };

이런 종류의 초기화가 실제 코드에서 많이 사용되는지 궁금합니다. 이 변경으로 인해 얼마나 많은 코드가 손상됩니까? 코드가 영향을받는 경우 코드에서이 문제를 해결하는 데 많은 노력이 필요합니까?


참고로 n3225의 8.5.4 / 6을 참조하십시오.

축소 변환은 암시 적 변환입니다.

  • 부동 소수점 유형에서 정수 유형으로 또는
  • long double에서 double 또는 float로 또는 double에서 float로 소스가 상수 표현식이고 변환 후 실제 값이 표현할 수있는 값의 범위 내에있는 경우를 제외하고 (정확히 표현할 수없는 경우에도)
  • 정수 유형 또는 범위가 지정되지 않은 열거 유형에서 유동 소수점 유형으로, 단, 소스가 상수 표현식이고 변환 후 실제 값이 대상 유형에 맞고 원래 유형으로 다시 변환 될 때 원래 값을 생성하는 경우를 제외하고, 또는
  • 정수 유형 또는 범위가 지정되지 않은 열거 유형에서 원본 유형의 모든 값을 나타낼 수없는 정수 유형으로, 단, 소스가 상수 표현식이고 변환 후 실제 값이 대상 유형에 맞고 다음과 같은 경우 원래 값을 생성합니다. 원래 유형으로 다시 변환됩니다.


답변

GCC를 사용했을 때이 주요 변경 사항이 발생했습니다. 컴파일러는 다음과 같은 코드에 대해 오류를 인쇄했습니다.

void foo(const unsigned long long &i)
{
    unsigned int a[2] = {i & 0xFFFFFFFF, i >> 32};
}

기능 void foo(const long long unsigned int&):

오류 : 변환을 축소 (((long long unsigned int)i) & 4294967295ull)에서 long long unsigned intunsigned int} {내부

오류 : 변환을 축소 (((long long unsigned int)i) >> 32)에서 long long unsigned intunsigned int} {내부

다행히도 오류 메시지는 간단했고 수정은 간단했습니다.

void foo(const unsigned long long &i)
{
    unsigned int a[2] = {static_cast<unsigned int>(i & 0xFFFFFFFF),
            static_cast<unsigned int>(i >> 32)};
}

코드는 외부 라이브러리에 있었으며 한 파일에 두 번만 발생했습니다. 주요 변경 사항이 많은 코드에 영향을 미칠 것이라고 생각하지 않습니다. 초보자는 수도 얻을 , 혼동 하지만.


답변

지난 12 년 동안 내가 작성한 C ++ 코드에 이런 종류의 문제가 있다는 사실을 알게되면 놀라고 실망 할 것입니다. 그러나 대부분의 컴파일러는 내가 무언가를 놓치지 않는 한 모든 컴파일 시간 “좁아짐”에 대한 경고를 뿌렸을 것입니다.

이것 또한 축소 변환입니까?

unsigned short b[] = { -1, INT_MAX };

그렇다면 부동 유형에서 정수 유형으로의 예보다 조금 더 자주 올 수 있다고 생각합니다.


답변

누군가가 다음과 같은 것에 걸리더라도 나는 그렇게 놀라지 않을 것입니다.

float ra[] = {0, CHAR_MAX, SHORT_MAX, INT_MAX, LONG_MAX};

(내 구현에서 마지막 두 개는 int / long으로 다시 변환 될 때 동일한 결과를 생성하지 않으므로 좁아집니다)

그래도이 글을 쓴 기억이 없습니다. 한계에 대한 근사치가 무언가에 유용한 경우에만 유용합니다.

이것은 적어도 모호하게 그럴듯 해 보입니다.

void some_function(int val1, int val2) {
    float asfloat[] = {val1, val2};    // not in C++0x
    double asdouble[] = {val1, val2};  // not in C++0x
    int asint[] = {val1, val2};        // OK
    // now do something with the arrays
}

그러나 그것은 완전히 설득력이 없습니다. 왜냐하면 내가 정확히 두 개의 값을 가지고 있다는 것을 안다면 왜 그것들을 배열이 아닌 배열에 넣 float floatval1 = val1, floatval1 = val2;습니까? 하지만 왜 컴파일 (정밀도 손실이 프로그램에 대해 허용 가능한 정확도 범위 내에서 작동하는 경우)해야하는 이유는 float asfloat[] = {val1, val2};무엇입니까? 어느 쪽이든 두 개의 int에서 두 개의 float를 초기화하고 있는데, 한 경우에 두 개의 float가 집계의 구성원이되는 것입니다.

특정 구현에서 소스 유형의 모든 값을 대상 유형으로 표현할 수 있고 원래 값으로 다시 변환 할 수 있지만 상수가 아닌 표현식으로 인해 축소 변환이 발생하는 경우 특히 가혹 해 보입니다.

char i = something();
static_assert(CHAR_BIT == 8);
double ra[] = {i}; // how is this worse than using a constant value?

버그가 없다고 가정하면 아마도 수정은 항상 변환을 명시 적으로 만드는 것입니다. 매크로로 이상한 일을하지 않는 한, 배열 이니셜 라이저는 배열의 유형에 가깝거나 적어도 템플릿 매개 변수에 종속 될 수있는 유형을 나타내는 무언가에 가깝다고 생각합니다. 따라서 캐스트는 장황하면 쉬울 것입니다.


답변

내가 만난 실제 사례 :

float x = 4.2; // an input argument
float a[2] = {x-0.5, x+0.5};

숫자 리터럴은 내재적 double으로 승격을 유발합니다.


답변

CFLAGS에 -Wno-narrowing을 추가해보십시오. 예를 들면 다음과 같습니다.

CFLAGS += -std=c++0x -Wno-narrowing

답변

축소 변환 오류는 암시 적 정수 승격 규칙과 잘못 상호 작용합니다.

다음과 같은 코드 오류가 발생했습니다.

struct char_t {
    char a;
}

void function(char c, char d) {
    char_t a = { c+d };
}

축소 변환 오류가 발생합니다 (표준에 따라 정확함). 그 이유는 것입니다 cd로 승진 암시 적으로 int그 결과가 int이니셜 라이저 목록의 문자에 다시 축소 할 수 없습니다.

OTOH

void function(char c, char d) {
    char a = c+d;
}

물론 여전히 괜찮습니다 (그렇지 않으면 모든 지옥이 풀릴 것입니다). 하지만 놀랍게도

template<char c, char d>
void function() {
    char_t a = { c+d };
}

정상이며 c와 d의 합이 CHAR_MAX보다 작 으면 경고없이 컴파일됩니다. 나는 여전히 이것이 C ++ 11의 결함이라고 생각하지만 거기에있는 사람들은 그렇지 않다고 생각합니다. 아마도 암시 적 정수 변환 (사람들이 코드를 작성했을 때의 과거의 유물)을 제거하지 않고는 고치기가 쉽지 않기 때문일 것입니다. 같은 char a=b*c/d과가 작동 할 것으로 예상 경우에도 (B * C)> CHAR_MAX) 또는 아마도 좋은 일이다 변환 오류를 (축소).


답변

이 기능에 대한 실제 경험을 통해 gcc가 C ++ 03 코드 기반을 C ++ 11로 이식하는 실제 생활의 고통으로 인해 많은 경우에 오류에서 경고로 좁혀 졌음을 보여 주었기 때문에 이는 실제로 큰 변화였습니다. gcc 버그 보고서에서 다음 주석을 참조하십시오 .

표준은 “적합한 구현이 최소한 하나의 진단 메시지를 발행해야한다”는 것만 요구하므로 경고와 함께 프로그램을 컴파일하는 것이 허용됩니다. Andrew가 말했듯이 -Werror = narrowing을 사용하면 원하는 경우 오류를 만들 수 있습니다.

G ++ 4.6에서 오류가 발생했지만 많은 사람들 (나 자신을 포함)이 대규모 C ++ 03 코드베이스를 C ++ 11로 컴파일 할 때 가장 일반적으로 발생하는 문제 중 하나가 발생하는 축소 변환을 발견했기 때문에 4.7에 대해 의도적으로 경고로 변경되었습니다 . char c [] = {i, 0}; (나는 char 범위 내에 만있을 것입니다) 오류를 일으켜 char c [] = {(char) i, 0}로 변경해야했습니다.