gpt4 book ai didi

c++ - 除了运算符优先级之外,额外的括号何时会产生影响?

转载 作者:行者123 更新时间:2023-12-01 17:58:55 26 4
gpt4 key购买 nike

C++ 中的括号用在很多地方:例如在函数调用和分组表达式中覆盖运算符优先级。 除了非法的额外括号 (例如围绕函数调用参数列表),C++ 的一般但不是绝对的规则是 额外的括号永远不会伤害 :

5.1 主要表达式 [expr.prim]

5.1.1 一般 [expr.prim.general]

6 A parenthesized expression is a primary expression whose type and value are identical to those of the enclosed expression. The presence of parentheses does not affect whether the expression is an lvalue. The parenthesized expression can be used in exactly the same contexts as those where the enclosed expression can be used, and with the same meaning, except as otherwise indicated.



问题 :除了覆盖基本运算符优先级之外,在哪些上下文中额外的括号会改变 C++ 程序的含义?

注意 : 我考虑了 的限制指向成员的指针 语法为 &qualified-id没有括号超出范围,因为它 restricts syntax而不是允许具有不同含义的两种语法。同样,使用 预处理器宏定义中的括号 还可以防止不必要的运算符优先级。

最佳答案

TL; 博士

在以下上下文中,额外的括号会改变 C++ 程序的含义:

  • 防止依赖于参数的名称查找
  • 在列表上下文中启用逗号运算符
  • 令人烦恼的解析的歧义解析
  • decltype 中推断引用性表达式
  • 防止预处理器宏错误

  • 防止依赖参数的名称查找

    正如标准的附录 A 中详述的, post-fix expression形式 (expression)primary expression ,但不是 id-expression ,因此不是 unqualified-id .这意味着在形式为 (fun)(arg) 的函数调用中阻止了依赖于参数的名称查找。与传统形式相比 fun(arg) .

    3.4.2 依赖参数的名称查找 [basic.lookup.argdep]

    1 When the postfix-expression in a function call (5.2.2) is an unqualified-id, other namespaces not considered during the usual unqualified lookup (3.4.1) may be searched, and in those namespaces, namespace-scope friend function or function template declarations (11.3) not otherwise visible may be found. These modifications to the search depend on the types of the arguments (and for template template arguments, the namespace of the template argument). [ Example:


    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
    }

    —end example ]



    在列表上下文中启用逗号运算符

    逗号运算符在大多数类似列表的上下文(函数和模板参数、初始值设定项列表等)中具有特殊含义。形式的括号 a, (b, c), d与常规形式 a, b, c, d 相比,在这种情况下可以启用逗号运算符其中逗号运算符不适用。

    5.18 逗号运算符 [expr.comma]

    2 In contexts where comma is given a special meaning, [ Example: in lists of arguments to functions (5.2.2) and lists of initializers (8.5) —end example ] the comma operator as described in Clause 5 can appear only in parentheses. [ Example:


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

    has three arguments, the second of which has the value 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 歧义解析 [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 ]



    一个著名的例子是 Most Vexing Parse ,一个由 Scott Meyers 在他的 Effective STL 的第 6 项中普及的名字书:
    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允许推断引用(左值和右值引用)。规则区分 decltype(e)decltype((e))表达式:

    7.1.6.2 简单类型说明符 [dcl.type.simple]

    4 For an expression e, the type denoted by decltype(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 by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;

    — otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

    — otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

    — otherwise, decltype(e) is the type of e.

    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 ]


    decltype(auto)的规则对于初始化表达式的 RHS 中的额外括号具有类似的含义。这是来自 C++FAQ 的示例和 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

    第一个返回 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 的不良副作用)
  • 关于c++ - 除了运算符优先级之外,额外的括号何时会产生影响?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24116817/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com