연산자 우선 순위 외에 추가 괄호가 언제 영향을 미칩니 까?
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 There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion (5.2.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.
8.2 Ambiguity resolution [dcl.ambig.res]
1 The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8, the resolution is to consider any construct that could possibly be a declaration a declaration. [ Note: A declaration can be explicitly disambiguated by a nonfunction-style cast, by an = to indicate initialization or by removing the redundant parentheses around the parameter name. —end note ] [ Example:
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
}
—end example ]
A famous example of this is the Most Vexing Parse, a name popularized by Scott Meyers in Item 6 of his Effective STL book:
ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile), // warning! this doesn't do
istream_iterator<int>()); // what you think it does
This declares a function, data
, whose return type is list<int>
. The function data takes two parameters:
- The first parameter is named
dataFile
. It's type isistream_iterator<int>
. The parentheses arounddataFile
are superfluous and are ignored. - The second parameter has no name. Its type is pointer to function taking nothing and returning an
istream_iterator<int>
.
Placing extra parentheses around the first function argument (parentheses around the second argument are illegal) will resolve the ambiguity
list<int> data((istream_iterator<int>(dataFile)), // note new parens
istream_iterator<int>()); // around first argument
// to list's constructor
C++11 has brace-initializer syntax that allows to side-step such parsing problems in many contexts.
Deducing referenceness in decltype
expressions
In contrast to auto
type deduction, decltype
allows referenceness (lvalue and rvalue references) to be deduced. The rules distinguish between decltype(e)
and decltype((e))
expressions:
7.1.6.2 Simple type specifiers [dcl.type.simple]
4 For an expression
e
, the type denoted bydecltype(e)
is defined as follows:— if
e
is an unparenthesized id-expression or an unparenthesized class member access (5.2.5),decltype(e)
is the type of the entity named bye
. If there is no such entity, or ife
names a set of overloaded functions, the program is ill-formed;— otherwise, if
e
is an xvalue,decltype(e)
isT&&
, whereT
is the type ofe
;— otherwise, if
e
is an lvalue,decltype(e)
isT&
, whereT
is the type ofe
;— otherwise,
decltype(e)
is the type ofe
.The operand of the decltype specifier is an unevaluated operand (Clause 5). [ Example:
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 ] [ Note: The rules for determining types involving
decltype(auto)
are specified in 7.1.6.4. —end note ]
The rules for decltype(auto)
have a similar meaning for extra parentheses in the RHS of the initializing expression. Here's an example from the C++FAQ and this related 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
The first returns string
, the second returns string &
, which is a reference to the local variable str
.
Preventing preprocessor macro related errors
There is a host of subtleties with preprocessor macros in their interaction with the C++ language proper, the most common of which are listed below
- using parentheses around macro parameters inside the macro definition
#define TIMES(A, B) (A) * (B);
in order to avoid unwanted operator precedence (e.g. inTIMES(1 + 2, 2 + 1)
which yields 9 but would yield 6 without the parentheses around(A)
and(B)
- using parentheses around macro arguments having commas inside:
assert((std::is_same<int, int>::value));
which would otherwise not compile - using parentheses around a function to protect against macro expansion in included headers:
(min)(a, b)
(with the unwanted side effect of also disabling ADL)
In general, in programming languages, "extra" parentheses implies that they are not changing the syntactical parsing order or meaning. They are being added to clarify the order (operator precedence) for the benefit of people reading the code, and their only effect would be to slightly slow the compile process, and reduce human errors in understanding the code (probably speeding up the overall development process).
If a set of parentheses actually changes the way an expression is parsed, then they are by definition not extra. Parentheses that turn an illegal/invalid parse into a legal one are not "extra", although that may point out a poor language design.
'IT박스' 카테고리의 다른 글
투명한 배경이 기기에 검은 색 배경을 보여주는 iOS 앱 아이콘 (0) | 2020.09.06 |
---|---|
C # 코드를 동적으로 평가하려면 어떻게해야합니까? (0) | 2020.09.06 |
C #에서 SqlDataReader를 사용하여 행 수를 얻는 방법 (0) | 2020.09.06 |
swf 파일을 어떻게 디 컴파일합니까 [closed] (0) | 2020.09.06 |
x64 Java에서 int보다 긴 이유는 무엇입니까? (0) | 2020.09.06 |