2010-10-20

pre-Batavia mailingの簡易レビュー

ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee

N3139: An Incomplete Language Feature

関数の実引数に、空の初期化リストを渡せない問題の修正。以下のようなコードがwell-formedになる。

struct S
{
    S( std::initializer_list<int> ) ;
} ;

// デフォルト引数に空の初期化リスト
void f( S s = { } ) ;

int main()
{
    // 関数の実引数に空の初期化リスト
    f( { } ) ;
}

N3140: Cleanup of Pair and tuple

C++0xの新機能に対応させようとしたら、std::pairとstd::tupleの文面がだいぶおかしくなってしまったので、これを統一して修正。

N3142: Adjustments to constructor and assignment traits

has_*_constructorとhas_*_assignというtraitsは、あたかもコア言語的な響きがする。しかし、ライブラリ作者としては、ある型Tがコンストラクターを持っているかどうかは、別にどうでもよい。ライブラリ作者が知りたいこととは、ある型Tに対して、T()とか、T(U)などと書いてよいかどうかである。したがって、hasという名前は相応しくない。

すべてのhas_*_constructorとhas_*_assignという名前のtraitsを、is_*_constructibleと、is_*_assignableという名前に変える提案。

N3143: Proposed wording for US 90

現行のstd::forwardの定義では、以下のコードがコンパイルできない。

#include <utility>

struct Base { Base(Base&&); };

struct Derived
: private Base
{
    Derived(Derived&& d)
        : Base(std::forward<Base>(d)) { }
};

// error: forward can not access Derived's private Base

なぜならば、DerivedからBaseへのキャストは、Derivedクラスの外からは、アクセス指定の制限のため、行えない。すなわち、std::forwardの中では、行えない。この問題は、std::forwardを、lvalue referenceとrvalue referenceでオーバーロードすることによって解決する。また、std::moveのreutrnに、static_castが抜けているので、あわせて修正。

やはりstd::moveのreturnにstatic_castが抜けているのは規格の間違いだったのか。だいぶ前にstd::moveを自分で実装して、おかしいと思ったのだ。

N3144: Wording for US 84

throw_with_nestedの文面の修正。Tがnested_exceptionを継承していない場合、Tとnested_exceptionを継承したクラスが、forwardによって構築されて投げられるらしい。実装例はこんな感じだろうか。attributeを消し、using宣言をtypedefに置き換えれば、gccで動くことを確認。最近書くコードは、こんなのばかりだ。まったくもって実務的ではない。

namespace std {

namespace detail {
template < typename T >
struct unspecified_exception
    : public std::remove_cv< typename std::remove_reference<T>::type >::type,
      public std::nested_exception
{
    template < typename U >
    unspecified_exception( U && u )
        : T( std::forward<U>(u) )
    { }
} ;

template < bool b >
struct throw_with_nested_impl
{
    template < typename T >
    [[noreturn]] static void invoke( T && t )
    {
        throw std::forward<T>(t) ;
    }
} ;

template<>
struct throw_with_nested_impl<false>
{
    template < typename T >
    [[noreturn]] static void invoke( T && t )
    {
        using type = typename std::remove_cv< typename std::remove_reference<T>::type >::type ;
        static_assert( std::is_class<type>::value, "throw_with_nested:T must be a non-union class type.") ;
        throw unspecified_exception<type>( std::forward<T>(t) ) ;
    }
} ;

} // namespace detail

template < typename T >
[[noreturn]] void throw_with_nested( T&& t )
{


    using plain_T = typename std::remove_reference<T>::type ;
    constexpr bool b = std::is_base_of<
        std::nested_exception,
        plain_T >::value ;
    detail::throw_with_nested_impl<b>::invoke( std::forward<T>(t) ) ;
}

} // namespace std

N3145:Deprecating unary/binary_function

名前通り。

N3146:Recommendations for extended identifier characters for C and C++

C++0xでは、Basic source character set外の文字を、識別子として使うことを許可した。具体的にどのような文字が許可されるのかは、実装に依存するが、規格での推奨事項(空白文字は好ましくないなど)を付け加える提案。

N3148 - throw() becomes noexcept (Version 2)
N3149 - From Throws Nothing to noexcept
N3150 - Removing non-empty dynamic exception specifications from the library

名前通り。throw()をnoexceptで置き換えと、Throws: Nothingをnoexceptに変更することと、空ではない動的例外指定を削除。

N3151: Keywords for override control
N3163: Override Control Using Contextual Keywords

Rapperswill会議において、attributeのbase_check、override、hiding、finalは、キーワードで置き換えることが、投票により決定した。問題は、キーワードをどうするかということだ。もちろん、キーワードは、既存のコードの互換性を確保するため、絶対に使われていないような名前でなければならない。そのため、ML上ではbikeshedding(どのように自転車小屋を建てるべきかという、正直どうでもいい議論)が盛んであった。

キーワード案が採用されるならば、とてつもなくuglyなキーワードが採用されることは確実である。ここで、Contextual Keyword(文脈依存キーワード)という案がある。これは、ある名前を、ある特定の場所でのみ、キーワードとして扱うというハックである。JavaやC#が、すでにこれをやっている。このようなハックは、コンパイラーや文法ハイライトの実装を難しくする。ただし、C++はすでに、他の言語とは比べものにならないぐらい実装が難しい言語である。この程度の実装は、他のもっと難しい昨日の実装に比べれば、屁みたいなものである。それに、このような実装をすれば、ユーザー側の負担は全くない。既存のコードを再コンパイルするコストは計り知れないのである。

N3152: Progress guarantees for C++0x

C++0xは、マルチスレッドとデータ競合に関して、Progress guaranteesというものを、全く定義していない。これでは、プログラマがまともにコードを書くことなど不可能ではないか。というNBコメントに対して、progress guaranteesというものを厳格に定義することは、非常に難しいということを説明したペーパー。

N3154: US 19: Ambiguous use of "use"

規格の文面では、useという言葉が使われている。ところで、3.2では、useという言葉を、特別な意味を表す用語として定義している。しかし、文面では、useを一般の英単語としても使っており、区別がつかない。そのため、3.2のuseを、odr-useという特別な用語に置き換えることにする。3.2の意味でuseという単語を使っている文面も、odr-useに置き換える。

N3155 - More on noexcept for the language support library
N3156 - More on noexcept for the diagnostics library
N3157 - More on noexcept for the General Utilities Library

既存の標準ライブラリの中で、noexcept指定できる関数を模索。

N3158: Missing preconditions for default-constructed match_result objects

名前通り。

N3164: Adjusting C++ Atomics for C Compatibility

C1XとC++0xのatomicの互換性の向上

N3165: Allocator Requirements: Alternatives to US88

allocator_traitsの導入によって、allocatorのrequirementsが大幅に減った。では、実際に規格上のrequirementsも弱めるべきか。C++03との互換性はどうするのか。といったことを論じている。このペーパーにおける提案は、LegacyAllocator Requirementsを付け加えるというものである。ちなみに、legacy_allocator_adaptorというwrapper classを追加しようというアイディアは、却下された。

N3166=10-0156 - Destructors default to noexcept

例外指定のないデストラクターは、デフォルトでnoexcept(true)にしようという提案。一般に、デストラクターから例外を投げるべきではない。すでにC++98の段階で、、STLの全クラスは、デストラクターからの例外のthrowを禁止されている。また、STLに渡すテンプレート実引数の型も、デストラクターから例外をthrowしてはならないと定められている。C++0xでは、デストラクターは明示的にnoexcept(false)などを指定した場合のみ、例外を外に投げられるようになる。

N3167=10-0157 - Delete operators default to noexcept

delete演算子を、デフォルトでnoexceptにする提案。当然、deallocation functionも、この影響を受けて、デフォルトでnoexceptになる。

N3168: Problems with Iostreams Member Functions (Amended from US 137)

また誰にも使われないiostreamの修正か!

N3169: A Few Small Library Issues

これも名前通り。

N3170: Clarifying C++ Futures

futureをどのような目的で使うかということに関して、委員の間でも、意見の不一致がみられた。そこで、futureをどのような場合に使うかという例を示す。また、小さな文面の修正も含んでいる。

N3171: Proposed resolution for US104: Allocator-aware regular expressions

regexがbasic_stringに指定するアロケーターに関して、意味を明確化。

N3172: Allocators for stringstream (US140)

stringstreamがbasic_stringに指定するアロケーターに関して、意味を明確化。

N3173: Terminology for constructing container elements (US115)

コンテナーの文面で、要素の構築に関して、CopyConstructibleとMoveConstructible、引数による構築を、事実上、再定義してしまっている。これを、あらかじめ定義された用語を使うように修正。

N3153: Implicit Move Must Go

暗黙的なムーブコンストラクターとムーブ代入演算子の生成は、C++03のコードとの互換性の問題を引き起こす。ゆえに、規格から消すべきである。題して、「暗黙のムーブは死ぬべき」

N3174: To move or not to move

ムブるべきか、ムブらざるべきか、それが問題だ。ここでは、暗黙に生成されるコピーとムーブによる問題点を列挙している。筆者は、たとえ暗黙のムーブがなかったとしても、暗黙のコピーにより、問題は残ることを論じた上で、現状のFCDの文面を保持するべきだと結論している。また、暗黙のコピーは、ユーザー定義のコピー、ムーブ、デストラクターがある場合、生成をdeprecatedにするべきだと提案している。

つまり、この問題は暗黙のムーブだけではなく、暗黙のコピーにも起こりえるのだ。暗黙のムーブが生成されない状況では、暗黙のコピーも、本来、生成されるべきではない。いまからコピーに対してそのような変更を行うのは無理なので、せめてdeprecatedにするべきだと提案している。

N3178: emplace Broken for Associative Containers

現在のemplaceの定義では、連想コンテナには適用できない。この修正は、現時点では、容易ではない。さてどうするかという短いペーパー。そもそも、STLのコンテナーは、現代からみれば、設計が悪いのだ。だからemplaceなんてものが必要になる。やれやれ。互換性ハ神聖ニシテ侵スヘカラス。

N3179: Move and swap for I/O streams (US138)

basic_istream, basic_ostream, basic_iostreamにおいては、ムーブコンストラクターは、実はムーブしない。ムーブ代入演算子も、実はムーブしない。swapも、実はswapしない。しかも、これらの関数はprotectedであり、ユーザーコードからは使用できない。

しかし、これらのクラスはabstract classではないし、ユーザーコードから使用すべき妥当な理由もある。まあ、これもiostreamだ。したがって誰にも省みられることのない修正と言える。

1 comment:

Anonymous said...

> odr-useという特別な用語に置き換えることにする。

また翻訳の難関が増えましたねぇ。