연산자 우선 순위 외에 추가 괄호는 언제 영향을 미칩니 까?


91

C ++의 괄호는 여러 위치에서 사용됩니다. 예를 들어 연산자 우선 순위를 재정의하는 함수 호출 및 그룹화 표현식에서. 그렇다 불법 여분의 괄호에서 (예 : 주변에 함수 호출 인수 목록과 같은), C의 일반적인 - 그러나 absolute-하지 규칙 ++ 것입니다 추가 괄호 해치지 않을 :

5.1 기본 표현식 [expr.prim]

5.1.1 일반 [expr.prim.general]

6 괄호로 묶인 표현식은 유형과 값이 괄호 안에있는 표현식과 동일한 기본 표현식입니다. 괄호가 있어도 표현식이 lvalue인지 여부에는 영향을주지 않습니다. 괄호로 묶인 표현식은 괄호로 묶인 표현식을 사용할 수있는 경우와 정확히 동일한 컨텍스트에서 사용할 수 있으며 달리 표시된 경우를 제외하고 동일한 의미로 사용할 수 있습니다 .

질문 : 기본 연산자 우선 순위를 재정의하는 것 외에 추가 괄호가 C ++ 프로그램의 의미를 변경하는 컨텍스트는 무엇입니까?

참고 : 멤버에 대한 포인터 구문 의 제한은 &qualified-id괄호가없는 것으로 간주합니다. 이는 다른 의미를 가진 두 구문을 허용하는 대신 구문제한 하기 때문 입니다. 마찬가지로 전 처리기 매크로 정의 내 에서 괄호를 사용하면 원치 않는 연산자 우선 순위를 방지 할 수 있습니다.


"멤버에 대한 포인터에 대한 & (qualified-id) 해상도는 연산자 우선 순위의 응용 프로그램이라고 생각합니다." -- 왜 그런 겁니까? 에서 괄호를 생략하면 &(C::f)의 피연산자 &는 여전히 C::f입니까?

@hvd expr.unary.op/4: 멤버에 대한 포인터는 명시 적이 &사용되고 피연산자가 괄호로 묶이지 않은 정규화 된 ID 인 경우에만 형성됩니다 .
TemplateRex

그렇다면 연산자 우선 순위와 무슨 관련이 있습니까? (걱정하지 마라, 당신의 편집 문제는 해결됩니다.)

@hvd 업데이트 됨, 이 Q & A 에서 RHS와 LHS를 혼동 했습니다. 괄호는 ()멤버 포인터 선택자 에 대한 함수 호출의 우선 순위를 재정의하는 데 사용됩니다::*
TemplateRex

1
어떤 경우에 고려가 필요한지 좀 더 정확해야한다고 생각합니다. 예를 들어 C 스타일 캐스트 연산자 (컨텍스트에 관계없이)로 만들기 위해 유형 이름을 괄호로 묶는 것은 괄호로 묶인 표현식을 전혀 만들지 않습니다. 반면에 기술적으로 if 또는 while 이후의 조건 은 괄호로 묶인 표현식 이라고 말할 수 있지만 괄호는 여기서 구문의 일부이므로 고려해서는 안됩니다. 연산자 우선 순위가 포함되었는지 여부에 관계없이 괄호가 없으면 표현식이 더 이상 단일 단위로 구문 분석되지 않는 IMO도 마찬가지입니다.
Marc van Leeuwen 2014-06-10

답변:


112

TL; DR

추가 괄호는 다음 컨텍스트에서 C ++ 프로그램의 의미를 변경합니다.

  • 인수 종속 이름 조회 방지
  • 목록 컨텍스트에서 쉼표 연산자 활성화
  • 성가신 구문 분석의 모호성 해결
  • decltype표현의 참조 성 추론
  • 전 처리기 매크로 오류 방지

인수 종속 이름 조회 방지

표준의 부록 A에 자세히 설명 된대로 post-fix expression형식의 (expression)a primary expression는이지만 id-expression은 아니므로 unqualified-id. 즉 (fun)(arg), 기존 형식에 비해 형식의 함수 호출에서 인수 종속 이름 조회가 방지됩니다 fun(arg).

3.4.2 인수 종속 이름 조회 [basic.lookup.argdep]

1 함수 호출 (5.2.2)의 postfix-expression이 unqualified-id 인 경우 일반적인 비 한정 조회 (3.4.1) 중에 고려되지 않은 다른 네임 스페이스를 검색 할 수 있으며, 해당 네임 스페이스에서 네임 스페이스 범위 친구 함수 또는 보이지 않는 함수 템플릿 선언 (11.3)을 찾을 수 있습니다. 검색에 대한 이러한 수정 사항은 인수 유형 (템플릿 템플릿 인수의 경우 템플릿 인수의 네임 스페이스)에 따라 다릅니다. [ 예:

namespace N {
    struct S { };
    void f(S);
}

void g() {
    N::S s;
    f(s);   // OK: calls N::f
    (f)(s); // error: N::f not considered; parentheses
            // prevent argument-dependent lookup
}

-예제 종료]

목록 컨텍스트에서 쉼표 연산자 사용

쉼표 연산자는 대부분의 목록과 유사한 컨텍스트 (함수 및 템플릿 인수, 이니셜 라이저 목록 등)에서 특별한 의미를 갖습니다. a, (b, c), d이러한 컨텍스트 에서 양식의 괄호는 a, b, c, d쉼표 연산자가 적용되지 않는 일반 양식에 비해 쉼표 연산자를 활성화 할 수 있습니다.

5.18 쉼표 연산자 [expr.comma]

2 쉼표에 특별한 의미가 부여 된 문맥에서 [예 : 함수에 대한 인수 목록 (5.2.2) 및 이니셜 라이저 목록 (8.5)- 끝 예]에서 5 절에 설명 된 쉼표 연산자는 괄호 안에 만 표시 될 수 있습니다. [ 예:

f(a, (t=3, t+2), c);

세 개의 인수가 있으며 두 번째 인수의 값은 5입니다. —end example]

성가신 구문 분석의 모호성 해결

C 및 그 신비한 함수 선언 구문과의 역 호환성은 성가신 구문 분석으로 알려진 놀라운 구문 분석 모호성을 유발할 수 있습니다. 본질적으로, 선언으로 파싱 할 수있는 모든 것은 경쟁 파싱도 적용 되더라도 하나로 파싱됩니다.

6.8 모호성 해결 [stmt.ambig]

1 표현식 문과 선언을 포함하는 문법에 모호함이 있습니다 . 함수 스타일의 명시 적 유형 변환 (5.2.3)이있는 표현식 문은 맨 왼쪽 하위 표현식이 첫 번째 선언자가 ( . 이러한 경우 문은 선언입니다 .

8.2 모호성 해결 [dcl.ambig.res]

1 함수 스타일 캐스트와 6.8에 언급 된 선언 간의 유사성으로 인해 발생하는 모호성은 선언 컨텍스트에서도 발생할 수 있습니다 . 그 맥락에서 선택은 매개 변수 이름 주위에 중복 된 괄호 세트가있는 함수 선언과 이니셜 라이저로 함수 스타일 캐스트가있는 객체 선언 사이에서 선택됩니다. 6.8에 언급 된 모호함과 마찬가지로, 결의안은 선언이 될 수있는 모든 구성을 선언 으로 간주하는 것 입니다. [참고 : 선언은 비 함수 스타일 캐스트, 초기화를 나타내는 = 또는 매개 변수 이름 주위의 중복 된 괄호를 제거하여 명시 적으로 명확하게 할 수 있습니다. —end note] [예 :

struct S {
    S(int);
};

void foo(double a) {
    S w(int(a));  // function declaration
    S x(int());   // function declaration
    S y((int)a);  // object declaration
    S z = int(a); // object declaration
}

-예제 종료]

이것에 대한 유명한 예는 그의 효과적인 STL 책 의 항목 6에서 Scott Meyers가 대중화 한 이름 인 Most Vexing Parse 입니다.

ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile), // warning! this doesn't do
               istream_iterator<int>());        // what you think it does

이것은 data반환 유형이 인 함수를 선언합니다 list<int>. 함수 데이터는 두 개의 매개 변수를 사용합니다.

  • 첫 번째 매개 변수의 이름은 dataFile입니다. 유형은 istream_iterator<int>입니다. 주위의 괄호 dataFile는 불필요하며 무시됩니다.
  • 두 번째 매개 변수에는 이름이 없습니다. 그 유형은 아무것도 취하지 않고 istream_iterator<int>.

첫 번째 함수 인수 주위에 추가 괄호를 배치하면 (두 번째 인수 주위의 괄호는 불법 임) 모호성을 해결합니다.

list<int> data((istream_iterator<int>(dataFile)), // note new parens
                istream_iterator<int>());          // around first argument
                                                  // to list's constructor

C ++ 11에는 여러 컨텍스트에서 이러한 구문 분석 문제를 회피 할 수있는 중괄호 이니셜 라이저 구문이 있습니다.

decltype표현의 참조 성 추론

auto유형 추론 과 달리 decltype참조 (lvalue 및 rvalue 참조)를 추론 할 수 있습니다. 규칙은 decltype(e)decltype((e))표현식을 구분 합니다.

7.1.6.2 단순 유형 지정자 [dcl.type.simple]

도 4는, 발현을 위해 e, 타입 붙이고decltype(e) 다음과 같이 정의된다 :

e괄호로 묶지 않은 ID 표현식이거나 괄호로 묶지 않은 클래스 멤버 액세스 (5.2.5) 인 경우 decltype(e)는에서 명명 된 엔티티의 유형입니다 e. 그러한 엔티티가 없거나 e오버로드 된 함수 집합의 이름을 지정하면 프로그램이 잘못된 것입니다.

— 그렇지 않은 경우 exvalue이면 decltype(e)is T&&, 여기서 Tis 유형은 e;

— 그렇지 않으면, elvalue이면 decltype(e)is T&, 여기서 Tis 유형 e;

— 그렇지 않으면 decltype(e)유형입니다 e.

decltype 지정자의 피연산자는 평가되지 않은 피연산자입니다 (Clause 5). [ 예:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 0;   // type is const int&&
decltype(i) x2;           // type is int
decltype(a->x) x3;        // type is double
decltype((a->x)) x4 = x3; // type is const double&

—end example] [참고 : 관련 유형을 결정하는 규칙 decltype(auto)은 7.1.6.4에 지정되어 있습니다. —end note]

에 대한 규칙 decltype(auto)은 초기화 표현식의 RHS에서 추가 괄호에 대해 유사한 의미를 갖습니다. 다음은 C ++ FAQ관련 Q & A의 예입니다.

decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }  //A
decltype(auto) look_up_a_string_2() { auto str = lookup1(); return(str); } //B

첫 번째는 지역 변수에 대한 참조 인 string두 번째를 반환하고 두 번째는 반환 string &합니다 str.

전 처리기 매크로 관련 오류 방지

적절한 C ++ 언어와 상호 작용할 때 전 처리기 매크로에는 미묘한 부분이 많이 있으며, 가장 일반적인 것은 아래에 나열되어 있습니다.

  • 매크로 정의 내부 매크로 파라미터 괄호를 사용하여 #define TIMES(A, B) (A) * (B);순서대로하여 불필요한 조작 우선 순위를 피하기 위해 (예로 TIMES(1 + 2, 2 + 1)하는 9 산출하지만 주위의 괄호없이 6을 수득 할 (A)(B)
  • 내부에 쉼표가있는 매크로 인수 주위에 괄호 사용 : assert((std::is_same<int, int>::value));그렇지 않으면 컴파일되지 않음
  • 포함 된 헤더에서 매크로 확장을 방지하기 위해 함수 주위에 괄호 사용 : (min)(a, b)(ADL을 비활성화하는 원치 않는 부작용 포함 )

7
실제로 프로그램 의미를 변경하지는 않지만 모범 사례이며 컴파일러에서 내 보낸 경고에 영향을줍니다 . 표현식이 할당 인 경우 if/에 추가 괄호를 사용해야 while합니다. 예 : if (a = b)경고 (말했 ==습니까?), 반면에 if ((a = b))경고 없음.
CSQ

@Csq 감사합니다. 좋은 관찰이지만 이는 특정 컴파일러의 경고이며 표준에서 요구하지 않습니다. 나는 이것이이 Q & A의 언어 변호사 성격에 맞지 않는다고 생각합니다.
TemplateRex

합니까 (min)(a, b)(악 매크로는 min(A, B)) 인수 종속적 이름 조회 예방의 일부입니다?
Jarod42 2014-06-09

Jarod42 @ 난 그렇게 생각하지만,의는 생각 해보자 및 기타 악 매크로를 :-) 질문의 범위를 벗어난 것으로
TemplateRex

5
영업 이익과 TemplateRex는 ^ _ ^ 동일인하는 것으로 : @JamesKanze
Jarod42

4

일반적으로 프로그래밍 언어에서 "추가"괄호 는 구문 분석 순서 또는 의미를 변경 하지 않음을 의미합니다. 코드를 읽는 사람들의 이익을 위해 순서 (연산자 우선 순위)를 명확히하기 위해 추가되고 있으며, 그 유일한 효과는 컴파일 프로세스를 약간 늦추고 코드를 이해하는 사람의 실수를 줄이는 것입니다 (아마도 전체 개발 프로세스의 속도를 높임). ).

괄호 집합이 실제로 식이 구문 분석되는 방식을 변경 하면 정의에 따라 추가 되지 않습니다 . 잘못된 / 잘못된 구문 분석을 합법적 인 구문으로 바꾸는 괄호는 잘못된 언어 디자인을 지적 할 수 있지만 "추가"가 아닙니다 .


2
정확히,이뿐만 아니라 C ++의 일반 규칙 (질문의 표준 견적을 참조)이며, 달리 명시를 제외하고 . 이러한 "약점"을 지적하는 것이이 Q & A의 목적이었습니다.
TemplateRex
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.