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
- value categories (lvalue, rvalue, glvalue, prvalue, xvalue) classify expressions by their values
- order of evaluation of arguments and subexpressions specify the order in which intermediate results are obtained
- operator precedence defines the order in which operators are bound to their arguments
- alternative representations are alternative spellings for some operators
- operator overloading makes it possible to specify the behavior of the operators with user-defined classes.
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 references¶,
c++
中叫做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.
- integer literals are decimal, octal, hexadecimal or binary numbers of integer type.
-
character literals are individual characters of type
-
floating-point literals are values of type float, double, or long double
-
string literals are sequences of characters of type
-
boolean literals are values of type bool, that is true and false
nullptr
is the pointer literal which specifies a null pointer value (since C++11)- user-defined literals are constant values of user-specified type (since C++11)
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:
由于
typeid
、sizeof
、noexcept
、decltype
都是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的operandi+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 accessibleT::~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:
- cppreference Non-static member functions
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
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;