2011-04-14

post-Madrid mailingの簡易レビュー

post-Madrid mailingが公開された。

最新の現行ドラフト(Working Draft)はn3291。ただし、今回はFDISがでている。FDISはn3290となる。現行ドラフトとの違いは、変更点の差分表示がされていないということである。

Core Issue 355: Global-scope :: in elaborated-type-specifier

core issue 355に対する修正。規格の定義に従えば、struct ::A { } ;はill-formedであるが、現行のコンパイラーはすべてサポートしていることや、特に問題が見当たらないことを鑑みて、許可しようという変更。変更内容が膨大なため、ペーパーを作成した。

N3260: Consolidated corrections for a cluster of constexpr concerns

定数式に対する細かい変更。といっても、意味としては、だいぶ大きな変更になる。

core issue 1197 を解決する形で、constexprの配列を許可した。例えば、以下の様なことが可能だ。

constexpr int a[] = { 1, 2 } ;
constexpr int b = a[0] ;

以前は、配列の一部の操作が定数式とは認められなかったので、これができなかった。

core issue 1060も解決。以下の様なコードが、ill-formedになる。以前は不思議なことにwell-formedだった。

enum struct E { e = 1 ; } ;
int a[ E::e ] ; // FDIS以前はwell-formedだった

core issue 1100を解決。Integral constant expressionでは、conversion functionは一つでなければならない。コードで説明したほうが分かりやすい。

struct X
{
    constexpr X() { } 
    constexpr operator int() { return 1 ; } 
    constexpr operator long() { return 1 ; }
} ;

int main()
{
    constexpr X x ;
    constexpr int i = x ; // OK
    int a[x] ; // エラー、曖昧
}

N3262: Additional Core Language Issue Resolutions for Madrid

その他のFDISで修正されたcore issueリスト。

N3263 - More on noexcept for the Containers Library (revision)

コンテナーライブラリーでnoexcept指定するメンバーを追加。

n3264: Moved-from state

ムーブされた後のオブジェクトの状態を規定。標準ライブラリのオブジェクトがムーブされた後は基本的に、valid but unspecified stateになる。これは、オブジェクトの状態は規定されていないが、その型に対して規定されている操作はすべて行えるということを意味する。

たとえば、std::vectorのムーブ後のオブジェクトに対して、empty()を呼び出すことはできる。ただし、状態が規定されていないので、どのような値を返すのかは分からない。その他の操作も、すべて規定されているように動作する。もし、empty()がtrueだった場合、front()は使えないなどだ。

これに対し、MoveConstructibleやMoveAssignable要求を満たす型は、もう少しゆるい保証となる。なぜならば、これは標準ライブラリ外のユーザー定義の型にも適用されるからだ。たとえば、std::vectorにムーブ可能な型を要素として渡して、実際に要素のムーブを行う場合、その型はこれらの要求で定義される保証を満たさなければならない。

このふたつの要求で保証しなければならない動作は、ムーブ後の状態は未規定で構わないが、標準ライブラリが、その要求と共に指定する他の要求は、依然として満たさなければならないというものだ。

たとえば、標準ライブラリに渡す、ある型がMoveConstructibleかつDestructibleを満たさなければならないと規定されていた場合、その型のオブジェクトはムーブ後も履き可能でなければならない。もし仮に、Destructibleが指定されていない場合は、ムーブ後のオブジェクトは破棄できなくてもかまわない。なぜならば、Destructibleが要求されていないということは、標準ライブラリはオブジェクトの破棄を行わないからだ。その場合、破棄はユーザー側の責任であって、標準ライブラリ側の責任ではないからだ。

n3266: Revision 2 of: Proposed Resolution for CH 15: Double check copy and move semantics of classes due to new rules for default move constructors and assignment operators

ムーブコンストラクターとムーブ代入演算子のデフォルトの意味が変わったので、いくつかの標準ライブラリの挙動も変わってしまう。それに合わせた変更。

N3267 - A review of noexcept in the threads library

スレッド周りのライブラリーで、noexcept指定できるものはnoexcept指定する変更。

n3268: static_assert and list-initialization in constexpr functions

constexprが大幅に改良された。これまでは、constexpr関数の本体は、たったひとつのreturn文で構成されていなければならないとされていた。しかし、これはあまりにも制限が強すぎるので、いくつかの文が許可された。以下のように書くことができるようになった。

constexpr int f()
{
   ; // null statement
   static_assert(true) ; // static_assert-declaration
   typedef int type1 ; // typedef delcaration and alias-declaration
   using type2 = int ; // that do not define the classes or enumerations.
   using std::size_t ; // using declaration.
   using namespace std ; // using directive

   return 0 ; // and exactly one return statement.
}

return文をひとつだけしか書けないというのは、変わっていない。constexprコンストラクターも、return文がないという事を除けば、同じように制限が緩められる。

N3269: shared_future(future<R>&&) should be allowed to throw

名前のとおり、shared_future(future<R>&&)は例外を投げられるべきだという変更。

n3270: Variadic Templates: Wording for Core Issues 778, 1182, and 1183.

Variadic templatesでどうもあやふやだった部分を明確にした。

以下の様なコードがwell-formedになる。

template<class ...T> struct value_holder {
// Values is a non-type template parameter pack and a pack expansion
    template<T ...Values> apply { }; 
  };

以下はエラーになる。

// Error: Values expands template type parameter pack T within the same template parameter list
template <class ...T, T ...Values> struct static_array; 

n3271: Wording for Range-Based For Loop (Option #5)

range-based forでクラス型のイテレーターを得る際、まずメンバー関数begin()/end()を探し、見つからない場合は、std名前空間をassociated namespaceに付け加えたADLによる検索に切り替える。これは、オプション5と呼ばれていた提案である。

N3272: Follow-up on override control

class member name checkingから、hidingとexplicitを取り除く変更。名前を隠すことに対するチェックと、明示的なhidingとoverrideの修飾を要求する機能がなくなる。結果として、finalとoverrideだけが残る。

hidingとexplicitは、コンセンサスが得られなかったのだ。まだ時期尚早ということだ。惜しい気もするが、仕方がない。

n3276: US22/DE9 Revisited: Decltype and Call Expressions

decltypeの未評価オペランドにおける関数呼び出しの結果の型に、不完全型を許すようにした。これにより、以下の様なコードが書ける。

struct unique_type ; // 定義しない
unique_type f() ; // 定義しない

std::is_same< decltype( f() ), unique_type >::value ;

この例では、定義が必要ないのだ。なぜ不完全型が許可されていなかったかというと、そもそも未評価オペランドは、sizeof向けの定義だったので、当然、不完全型は許可されていなかったのだ。decltypeで、sizeof用の定義を使いまわしたのが問題だった。

N3277: Core issues 1194/1195/1199: References and constexpr

リファレンス型をリテラル型にする変更。これまでは、constexprで、仮引数や戻り値の型にリファレンスを許可するという目的で、いちいち個別に許可していたのだが、そもそもリファレンス自体をリテラル型にしてもいいじゃないかという発想から、このような変更に至った。

n3278: Recent Concurrency Issue Resolutions

concurrency周りの雑多な問題の解決。

n3279: Conservative use of noexcept in the Library

標準ライブラリの関数を見境無くnoexcept指定することへの警鐘。

C++ Freestanding and Conditionally Supported

<thread>をfreestanding headerにするには無理がありすぎるので、廃止。また、コンパイル時にスレッドをサポートしているかどうかを判定できるようにするプリプロセッサーマクロ、__STDCPP_THREADS__を追加。もし、環境が複数のスレッドをサポートしていれば、このマクロが定義される。

また、この決定にともなって、<ratio>と<chrono>もfreestanding headerではなくなる。

n3281: 692. Partial ordering of variadic class template partial specializations

このペーパーの以前のバージョンでは、variadic class templateのpartial orderingでは、関数のすべての仮引数が、partial orderingで考慮されることになっていた。しかし、そもそもpartial orderingを、明示的な実引数がないのに行うのは不自然である。そのため、明示的な実引数がある場合のみ行うように変更する。

これは、当然だと思う。2010年の3月の時点で、私はおかしいと思っていたのだ。本の虫: Template Parameter Packがゼロ個の場合でもpartial orderingで考慮されるかで疑問に思っていたことだ。

これは、かなり大きな変更だ。この変更により、本の虫: テンプレートパラメーターパックがゼロ個でも、Partial Orderingで考慮されるは、現状と一致しなくなる。これは大きな変更だ。本当に大きな変更だ。

template < typename T >
void f( T ) ; // #1
template < typename T, typename ... Types >
void f( T, Types ... args ) ;

int main()
{
    f( 0 ) ; // エラー、曖昧、FDIS以前では#1が選ばれた
} 

穴を塞いだJason Merrillには、感謝すべきなのか。exploitできなくなったので恨むべきなのか。

ただ、私がなんとなく感じていた、ゼロ個のtemplate parameter packにpartial orderingが適用されるということへの違和感は、正しかったことになる。してみれば、私も智慧も捨てたものではない。

n3282: Resolution for core issues 1207 and 1017

core issue 1207は、const修飾されたメンバー関数のtrailing-return-typeでのthisは、constではないという問題への修正である。以下の様なコードが問題になる。

struct X
{
    iterator f() { }
    const_iterator f() const { }
} ;

struct Y
{
    X x ;
    auto f() const -> decltype(x.f()) { return x.f() ; }
} ;

Y::fはconst修飾子がある。。そのため、thisはconst修飾されている。つまり、Y::fの関数の本体における式、x.f()の型は、const_iteratorである。ところが、trailing-return-typeは、影響を受けない。したがって、戻り値の型は、iteratorになってしまう。これはまずい。そのため、メンバー関数におけるthisの型は、const修飾子を考慮する。

core issue 1017は、これに比べると簡単な問題だ。以下の様なコードを許可する変更である。

struct X
{
    int member ;
} ;

struct Y
{
    decltype( X::member ) x ; // OK
} ;

これまでは、定義に従うと、クラスYにおけるX::memberは、(*this).X::memberという風に変換されてしまう。ここでいうthisとは、もちろんYのthisである。これは、当然エラーになってしまう。そのため、未評価式の中では、この変換を行わないように変更する。

n3283: Dependent Bases and the Current Instantiation: Wording for Core Issue 1043

これは、core issue 1043への対応である。

クラステンプレートにおけるname lookupでは、non-dependent nameのみを探す名前探索と、dependent nameもさがす名前探索がある。このTwo Phase Lookupは有名だ。ところで、以下の場合はどうなるのだろうか。

template<typename T> struct B {};
  struct C { typedef int type; };

  template<typename T>
  struct A : B<T>, C {
    template<typename U> type a(); // #1
    template<typename U> typename A<T>::type a(); // #2: different from #1?
  };

  template<typename T> template<typename U> typename A<T>::type
    A<T>::a() { ... } // defines #1 or #2?

unqualified-idであるtypeにたいしては、C::typeが選ばれる。では、qualified-idの場合はどうなるのか。#2は#1とは違う関数なのだろうか。それとも、同じ関数だから、エラーになるのだろうか。ましてや、その関数をクラス本体の外で定義する場合はどうなるのか。

これに対しては、non-dependent base memberが見つかるようにするとの合意が得られた。つまり、現行のgccは間違っている。EDGは正しい。

9 comments:

Anonymous said...

> ... valid but unspecified stateになる。これは、通常の操作は
> どうなるか分からないが、破棄可能かつ代入可能な状態である。

17.3.26 の定義をざっと訳すと以下のようになるかと。
「オブジェクトの不変条件は満たされオブジェクトに対する操作は型に対して
 定められたとおりに振舞うということを除き、不定なオブジェクトの状態。」
というわけで、「どうなるかわからない」ということはないですし、代入可能
とも限りません。

江添亮 said...

それはユーザー定義の型に対する要求です。

Anonymous said...

> それはユーザー定義の型に対する要求です。

17.6.4.15 "Moved-from state of library types" ではっきりと、特記のない
限り "valid but unspecified state" になるものという記述があるので、
ライブラリ内の型について定めたものだと読めます。

逆にユーザー定義型のほうが、 "MoveConstructible requirements",
"MoveAssignable requirements" に変更が入って、ライブラリが明示的に要求
する操作以外は「どうなるかわからない」を認めているようです。

江添亮 said...

ちゃんとn3264を読みましたか?
そもそも根本的に間違っています。

Anonymous said...

引き合いに出した 17.3.26, 17.6.4.15 ともに n3264 に含まれており、確認
できます。この 17.6.4.15 は FDIS では 17.6.5.15 になっていました。指摘
しているこちらの解釈は n3264 の Rationale にある "The version for
user-defined types is weaker" という記述とも合致します。

逆にお聞きしますが、「通常の操作はどうなるか分からないが、破棄可能かつ
代入可能な状態」というのは規格中のどの記述に対応するのでしょうか?

江添亮 said...

ある文章が規格に含まれているからと入って、それがすべてに適用出来るものではありません。
ある場所に「挙動は未定義である」と書かれていたとして、それが規格のあらゆる機能に対して適用出来ると思っているのですか?

「挙動は未定義である」という文章は、「これこれの条件を満たした場合に」という条件がつくのです。

17.3.26 [defns.valid]は、Valid but unspecifiedという状態の説明です。
オブジェクトの状態は未規定だが、その型に期待されている操作はすべて動作するという保証です。

17.6.5.15 [lib.types.movedfrom]は、標準ライブラリで定義されているオブジェクトの保障すべき挙動です。

MoveConstructibleとMoveAssignable要求に書いてあるのは、その要求を満たす型が保障すべき挙動です。

Anonymous said...

> ... と思っているのですか?

いいえ。そんなふうに思っていると取られるコメントは書いた覚えが
ないですし、見直してもどこを読んでそう思われてしまったのかわかりません
でした。すいません。

17.6.5.15 で標準ライブラリ定義の型のオブジェクトがムーブされた後の状態は
"valid but unspecified state" となることが定められ、 17.3.26 で
"valid but unspecified state" とは「オブジェクトの状態は未規定だが、
その型に期待されている操作はすべて動作する」状態であると定められて
います。この2つの定めをまとめると「標準ライブラリ定義の型の
オブジェクトがムーブされた後は、状態か未規定だが、その型に期待されている
操作はすべて動作する」ことが定められています。

「その型に期待されている操作はすべて動作する」と定められているのに、
このブログエントリの解説では「通常の操作はどうなるか分からないが、
破棄可能かつ代入可能」と、規格のどの記述によるものか不明な、異なった
記述がされている点について、最初のコメントで指摘したのです。

これでもまだこの解説(および別に立ててもらったエントリ)の記述「通常の
操作はどうなるか分からないが、破棄可能かつ代入可能な状態」が正しいと
おっしゃるのであれば、規格中のどの記述に対応するのか示していただけない
でしょうか?

江添亮 said...

通常の操作はどうなるか分からないというのは、まあ言葉のあやみたいなものです。

実際には、確かに操作はできます。

Anonymous said...

> ... まあ言葉のあやみたいなものです。

うーん。明らかに意味が違ってしまっていると思います。

n3264 にある US-85 (= LWG-1374) の提案内容には "... can be destroyed
and can be the destination of an assignment. Anything else is
radioactive. ..." という記述があったので、おそらくここを間違って拾って
しまわれたのでしょう。

あとはコメント欄でも、最初に返信してもらったコメントにより
"valid but unspecified state" について「それはユーザー定義の型に対する
要求です」などと返されているのも誤解の元になってしまいそうです。

日本語での解説として規格のかわりに参照される人も(残念ながら)多いのが
現状ですので、できれば修正しておいてほしいと思っています。