Skip to content

cppreference Expressions

An expression is a sequence of operators and their operands, that specifies a computation.

NOTE: 上述定义是从数学expression的角度出发来给出的定义,关于此可以参见Theory\Programming-language-construct\Basic-Language-construct\Operator-expression-statement.md,其中对这个话题进行了详细分析。

那c++语言中的expression是否仅限于此呢?不是的,在cppreference Value categories中给出了详细的说明:

Each C++ expression (an operator with its operands, a literal, a variable name, etc.)

显然,在c++中,我们可以将literal、variable name都看做是expression。

General

NOTE: python中没有value categories一说,相比之下,python的data model是简单地多的。其他的相关问题,python中都有。

NOTE: 上述后三条在原文中和前两条不在一起,我觉得它们是所有的operator都会涉及到的问题,所以我将它们放到了一起。

Operators

NOTE:

原文对c++语言中的operator的分类方法如下:

operator可以分为两大类:

  • Common operators
  • Special operators

Special operators可以进一步细分为:

  • Conversions
  • Memory allocation
  • Other

对c++语言中的operator的还可以采用另外的分类方法:

根据compile-time或run-time来对operator进行分类:

compile-time operator:

  • sizeof

  • decltype

......

在原文的Unevaluated expressions章节对此进行了说明。

Common operators

NOTE: python中assignment的含义是bind。

NOTE : python中叫做Attribute referencesc++中叫做member access。

Special operators

NOTE: 下面是对special operator的分类。

Conversions

NOTE: 放到了type system章节,参见C++\Language-reference\Basic-concept\Type-system\Type-conversion

Memory allocation

1、new expression allocates memory dynamically

2、delete expression deallocates memory dynamically

Other

operator 说明
constant expressions can be evaluated at compile time and used in compile-time context (template arguments, array sizes, etc), 非常重要的概念,在C++\Language-reference\Expressions\Constant-expressions中进行了介绍
sizeof 参见:
- “Unevaluated expressions”
alignof 参见:
- “Unevaluated expressions”
typeid 参见:
- “Unevaluated expressions”
throw-expression

Primary expressions

NOTE: primary expression的概念是比较重要的,因为有的时候,compiler的报错中,直接就使用primary expression。

The operands of any operator may be other expressions or primary expressions (e.g. in 1+2*3, the operands of operator + are the subexpression 2*3 and the primary expression 1).

NOTE: python expression中也有Primaries的概念

Primary expressions are any of the following:

1) Literals (e.g. 2 or "Hello, world")

2) Id-expressions

NOTE: 参见下面的“ID-expression”章节

3) Lambda-expressions (C++11)

4) Fold-expressions (C++17)

5) Requires-expressions (C++20)

Any expression in parentheses is also classified as a primary expression: this guarantees that the parentheses have higher precedence than any operator. Parentheses preserve value, type, and value category.

NOTE: 如何理解primary expression呢?我觉得应该从operand来理解,operand需要是primary expression;如果不满足这个条件,则compiler就会complain类似如下error:

expected primary-expression before ‘>’ token

C++\Language-reference\Basic-concept\Organization\Name-lookup\Dependent-name-lookup中讲述了上述错误的深层原因。

通过上述错误,能够加深我们对primary expression的理解。

Literals

Literals are the tokens of a C++ program that represent constant values embedded in the source code.

NOTE: python expression中也有Literals

Unevaluated expressions

The operands of the operators typeid, sizeof, noexcept, and decltype (since C++11) are expressions that are not evaluated (unless they are polymorphic glvalues and are the operands of typeid), since these operators only query the compile-time properties of their operands. Thus, std::size_t n = sizeof(std::cout << 42); does not perform console output.

NOTE:

由于typeidsizeofnoexceptdecltype都是compile-time operator( typeid比较特殊,上面这段话对此进行了补充说明),所以当它们的operand是expression的时候,这些operator仅仅会使用expression的compile-time property,所以这些expression是不会被evaluate的,这就是本节所描述的unevaluated expressions。

我们可以从对立面来理解它,以function-call operator为例:

#include <iostream>
int i = 1;
void F(int i)
{
std::cout<<i<<std::endl;
}
int main()
{
int i = 1;
F(i+1);
}

上述程序的输出为2,显然function-call operator的operand i+1被evaluated了,它是run-time的。

Example: sizeof

在文章C++\Language-reference\Expressions\Operators\sizeof.md中给出了sizeof和Unevaluated expressions之间的例子。

NOTE:

Unevaluated expressions and built-in comma operator

decltype 的 operand 可以是build-in comma operator构成的expression,build-in comma operator的第一个operand是discard value的,说明它是被evaluated的,那这要如何来进行理解呢?

NOTE: 上面这一段提示了我们:有些operator在compile-time进行计算的,而有些是在run-time进行计算的,可以将此作为对operator的分类方法;上面这一段对typeid进行了特殊说明,它表示typeid也可能是run-time。

Unevaluated operands are full expressions (since C++14)

The unevaluated operands are considered to be full expressions even though they are syntactically operands in a larger expression (for example, this means that sizeof(T()) requires an accessible T::~T). (since C++14)

NOTE: “for example, this means that sizeof(T()) requires an accessible T::~T”没有搞懂。

Requires-expressions (since C++20)

The requires-expressions are also unevaluated expressions. An invocation of an immediate function is always evaluated, even in an unevaluated operand.(since C++20)

NOTE: 各种特殊的规则,使得c++语言比较复杂。

Discarded-value expressions

NOTE: 参见Discarded-value-expressions章节

Full list of c++ operator

c++中有哪些operator?下面对此进行枚举:

在cppreference C++ Operator Precedence中枚举了几乎所有的c++ operator。

Supplement: id-expression

原文没有对id-expression进行深入说明,下面是对它的补充说明:

id-expression中的id的含义是identifier,在cppreference identifier的“In expressions”段中对它进行了详细的说明,它包含:

1) Suitably declared unqualified identifiers (e.g. n or cout)

2) Suitably declared qualified identifiers (e.g. std::string::npos)

在下面的章节中提及了id-expression:

Example 1

stackoverflow Why user-defined conversion is not implicitly taking place on the calling object

#include <iostream>
class A
{
public:
    void func() const
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

class B
{
public:
    // user-defined conversion operator to A
    operator A() const
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
        return a_;
    }
private:
    A a_;
};

int main()
{
    B b;
    static_cast<A>(b).func(); // call func() on temporary instance of A
    B b;
    // b.func(); // <-- error: 'class B' has no member named 'func'
}
// g++  test.cpp

A

Conversion isn't considered for member access (§5.2.5/2 [expr.ref]).

In either case, the id-expression shall name a member of the class or of one of its base classes

Here the id-expression is func()

So the compiler considers that func must be a member of B or a class that B derives from. Implicit conversion to other types that might have a func member is not considered.

Example 2

thegreenplace Dependent name lookup for C++ templates :

compiler默认情况下是将dependent name作为identifier,通过keyword typename来告诉compiler,这个name表示的是type,通过keyword template来告诉compiler,这个name表示的是template;