ツイッターで出題した未定義問題のお詫びと調査と解説について - paiza開発日誌


Abstract and Introduction


This paper proposes an order of evaluation of operands in expressions, directly supporting decades-old established and recommended C++ idioms. - P0145R3 Abstract


Order of expression evaluation is a recurring discussion topic in the C++ community. In a nutshell, given an expression such as f(a, b, c), the order in which the sub-expressions f, a, b, c (which are of arbitrary shapes) are evaluated is left unspecified by the standard. If any two of these sub-expressions happen to modify the same object without intervening sequence points, the behavior of the program is undefined. For instance, the expression f(i++, i) where i is an integer variable leads to undefined behavior, as does v[i] = i++. Even when the behavior is not undefined, the result of evaluating an expression can still be anybody’s guess. Consider the following program fragment:- P0145R3 1. INTRODUCTION



のような式が与えられると、部分式fabc(任意の形状)が評価される順序は、規格によってunspecifiedのままである。これらのサブ式のいずれか2つが、sequence pointなしに同オブジェクトを変更する場合、プログラムの動作はUBである。 例えば、式

f(i++,i) //(i is an integer variable)




#include <map>
int main() {
    std::map<int, int> m;
    m[0] = m.size(); // #1

♯1とマークされている文の評価後、マップオブジェクトmはどのように見えるべきだろうか? {{0、0}}または{{0、1}}だろうか。

A corroding problem

These questions aren’t for entertainment, or job interview drills, or just for academic interests. The order of expression evaluation, as it is currently specified in the standard, undermines advices, popular programming idioms, or the relative safety of standard library facilities. The traps aren’t just for novices or the careless programmer. They affect all of us indiscriminately, even when we know the rules. Consider the following program fragment: - P0145R3 2. A CORRODING PROBLEM


void f()
    std::string s = “but I have heard it works even if you don’t believe in it”;
    s.replace(0, 4, “”).replace(s.find(“even”), 4, “only”).replace(s.find(“ don’t”), 6, “”);
    assert(s == “I have heard it works only if you believe in it”);
The assertion is supposed to validate the programmer’s intended result. It uses “chaining” of member function calls, a common standard practice. This code has been reviewed by C++ experts world-wide, and published (The C++ Programming Language, 4th edition.) Yet, its vulnerability to unspecified order of evaluation has been discovered only recently by a tool. Even if you would like to blame the “excessive” chaining, remember that expressions of the form std::cout << f() << g() << h() usually result in chaining, after the overloaded operators have been resolved into function calls. It is the source of endless headaches. Newer library facilities such as std::future are also vulnerable to this problem, when considering chaining of the then() member function to specify a sequence of computation. The solution isn’t to avoid chaining. Rather, it is to fix the problem at the source: refinement of the language rules.- P0145R3 2. A CORRODING PROBLEM

assertマクロは、プログラマの意図を検証している。これは、メンバー関数呼び出しの「連鎖」を使用している*1このコードは、C++の専門家によって世界中でレビューされ、公開されている(The C++ Programming Language, 4th edition)。しかし、評価の不特定の順序に対する脆弱性は、ツールによって最近発見された。このような"過度な"連鎖をしなくても、例えば次のような式は関数呼び出しの連鎖である事に留意しなければならない。



Why now?

なぜ今なのか。A corroding problemと大体同じような事が扱われているので一部だけ。

The current rules have been in effect for more than three decades. So, why change them now? Well, a programming language is a set of responses to challenges of its time. Many of the existing rules regarding order of expression evaluation made sense when C was designed and in the constrained environment where C++ was originally designed and implemented. Some of the justifications probably still hold today. However, a living and evolving programming language cannot just hold onto inertia. - P0145R3 WHY NOW?


The language should support contemporary idioms. For example, using << as insertion operator into a stream is now an elementary idiom. So is chaining member function calls. The language rules should guarantee that such idioms aren’t programming hazards. We have library facilities (e.g. std::future) designed to be used idiomatically with chaining. Without the guarantee that the obvious order of evaluation for function call and member selection is obeyed, these facilities become traps, source of obscure, hard to track bugs, facile opportunities for vulnerabilities. - P0145R3 WHY NOW?


A Solution


We propose to revise C++ evaluation rules to support decades-old idiomatic constructs and programming practices. A simple solution would be to require that every expression has a well-defined evaluation order. That suggestion has traditionally met resistance for various reasons. Rather, this proposalsuggests a more targeted fix:

  • Postfix expressions are evaluated from left to right. This includes functions calls and member selection expressions.
  • Assignment expressions are evaluated from right to left. This includes compound assignments.
  • Operands to shift operators are evaluated from left to right.

In summary, the following expressions are evaluated in the order a, then b, then c, then d:
  1. a.b
  2. a->b
  3. a->*b
  4. a(b1, b2, b3)
  5. b @= a
  6. a[b]
  7. a << b
  8. a >> b
Furthermore, we suggest the following additional rule: the order of evaluation of an expression involving an overloaded operator is determined by the order associated with the corresponding built-in operator, not the rules for function calls. This rule is to support generic programming and extensive use of overloaded operators, which are distinctive features of modern C++.


  • 後置式は左から右に評価される。これには、関数呼び出しとメンバ選択式が含まれる。
  • 代入式は右から左へ評価される。これには、複合的(連続的)な割り当てが含まれる。
  • シフト演算子オペランドは左から右に評価される。


A second, subsidiary proposal replaces the evaluation order of function calls as follows: the function is evaluated before all its arguments, but any pair of arguments (from the argument list) is indeterminately sequenced; meaning that one is evaluated before the other but the order is not specified; it is guaranteed that the function is evaluated before the arguments. This reflects a suggestion made by some members of the Core Working Group. - P0145R3 A SOLUTION

2つ目の補助提案は、関数呼び出しの評価順序を次のように置き換える:関数は全ての引数より前に評価されるが、引数リスト中の任意の引数のペアは不確定に順序付けられる。これは、一方が他方の前に評価されるが、順序は指定されないことを意味する。これにより、関数が引数の前に評価されることが保証される。これは、Core Working Groupのメンバーによって提案されたことを反映している。



At the Fall 2014 meeting in Urbana, IL, Clark Nelson observed that the proposal does not suggest when side effects of postfix increment and postfix decrement are “committed”. Indeed, the current proposal does not suggest any particular modification to the sequencing of unary expressions. The primary reason is that we have not found a choice that will support an existing widely used programming idiom or nurture new programming techniques. Consequently, at this point, we do not propose any change to unary expressions. The side effects of unary expressions shall be committed before the next expression (if any) is evaluated if it is part of a binary expression or a function call. The sequencing order of unary expressions is not changed by this proposal. - P0145R3 POSTFIX INCREMENT AND DECREMENT

Clark Nelson氏は、2014年秋のUrbanaでの会議で、後置インクリメント、デクリメントの副作用がコミットされるかについて提案していないことを確認した。実際のところ、現在の提案は、単項式の順序付けに対する特別な変更を示唆していない。主な理由は、既存の広く使用されているプログラミングイディオムをサポートすべきか、新しいプログラミング技術を育成する事を優先すべきか、判断しかねるからである。したがって、この時点では、単項式への変更は提案しない。単項式の副作用は、次の式がある場合には、それがバイナリ式、または関数呼び出しの一部である場合に、評価される前にコミットされる。よって単項式の順序付けは、この提案によって変更されない。


*1:訳注:連鎖:=連続的なメンバ関数呼び出し。e.g. f().g().h()のような。