C ++ 0x에서 변환 범위를 좁 힙니다. 그것은 나뿐입니까, 아니면 이것이 큰 변화처럼 들립니까?


85

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

int a[] = { 1.0 };

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


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

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

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

1
이것이 내장 유형의 초기화에만 유효하다고 가정하면 이것이 어떻게 해를 끼치는 지 알 수 없습니다. 물론, 이로 인해 일부 코드가 손상 될 수 있습니다. 그러나 쉽게 고칠 수 있어야합니다.
Johan Kotlinski 2010

1
@John Dibling : 아니요, 대상 유형으로 값을 정확하게 나타낼 수있는 경우 초기화 형식이 잘못되지는 않습니다. (그리고 어쨌든 0이미 int있습니다.)
aschepler 2010

2
@Nim : 이것은 {중괄호 이니셜 라이저 내에서만 잘못된 형식 }이며 이들의 유일한 레거시 사용은 배열 및 POD 구조체에 대한 것입니다. 또한 기존 코드가 속한 위치에 명시 적 캐스트가있는 경우 중단되지 않습니다.
aschepler 2010

4
작업 문서에 나와 있듯이 @j_random_hacker int a = 1.0;는 여전히 유효합니다.
Johannes Schaub-litb

1
@litb : 감사합니다. 사실 이해할 수 있지만 실망 스럽습니다 .IMHO C ++ 시작부터 모든 축소 변환에 대해 명시적인 구문을 요구하는 것이 훨씬 낫습니다.
j_random_hacker

답변:


41

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

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


9

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

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

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

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


1
왜 이것이 코드에서 찾기에 드문 일이 아니라고 말하는지 이해가 안됩니다. USHRT_MAX 대신 -1 또는 INT_MAX 사용 사이의 논리는 무엇입니까? USHRT_MAX는 2010 년 말에 climits가 아니 었습니까?

7

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

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?

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


8
"내가 정확히 두 개의 값을 가지고 있다는 것을 안다면 왜 그것들을 배열에 넣어야하는지" -예를 들어 OpenGL과 같은 API가 그것을 요구하기 때문입니다.
Georg Fritzsche

7

내가 만난 실제 사례 :

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

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


1
그래서 float작성 하여 만드십시오 0.5f. ;)
underscore_d

1
@underscore_d floattypedef 또는 템플릿 매개 변수 인 경우 (적어도 정밀도 손실없이) 작동하지 않지만 요점은 작성된 코드가 올바른 의미 체계로 작동하고 C ++ 11에서 오류가되었다는 것입니다. 즉, "브레이킹 체인지"의 정의.
Jed

5

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

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

또는 C ++ 컴파일러의 경우 CPPFLAGS (물론 빌드 시스템 또는 Makefile에 따라 다름)
Mikolasan

4

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

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

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) 또는 아마도 좋은 일이다 변환 오류를 (축소).


정말 짜증나는 말도 안되는 다음과 같은 문제가 발생했습니다. unsigned char x; static unsigned char const m = 0x7f; ... unsigned char r = { x & m };<-{} 내부의 변환 축소. 정말? 그래서 operator &는 또한 unsigned chars를 int로 암시 적으로 변환합니까? 글쎄요, 그 결과는 여전히 unsigned char, argh로 보장됩니다.
Carlo Wood

" 암시 적 정수 변환 "프로모션?
curiousguy jul.

2

이 기능에 대한 실제 경험을 통해 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}로 변경해야했습니다.


1

GCC-4.7은 더 이상 변환 범위를 좁힐 때 오류를 제공하지 않고 대신 경고를 표시합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.