연산자 우선 순위 외에 추가 괄호는 언제 영향을 미칩니 까? 영향을주지 않습니다. 괄호로

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

5.1 기본 표현식 [expr.prim]

5.1.1 일반 [expr.prim.general]

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

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

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



답변

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을 비활성화하는 원치 않는 부작용 포함 )


답변

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

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


답변