C++0x の FDIS について、細かい部分で幾つか不満な点が見つかったので、メモしておきます。
本来なら FDIS を採択した Madrid meeting 以前に言うべきであった事ばかりで、機を逃した感じですが、
気づいておきながら何も主張しないよりはマシだと思ったので。
std::basic_string
の swap
が noexcept
指定されていない
http://d.hatena.ne.jp/gintenlabo/20110413/1302689739 にも書いたとおり、
move ctor や move 代入演算子は noexcept
指定されているので、 swap
も noexcept
指定するべきです。
英訳メモ(後で使うかも):
Functions std::basic_string::swap
and std::swap( basic_string&, basic_string& )
should be marked as noexcept
, because each of them has a wide contract, and a move ctor and a move assignment operator of std::basic_string
are marked as noexcept
.
char8_t
欲しい
本の虫: C++0xのUTF-8対応に問題あり
要は char16_t
や char32_t
と同じように、他の型と区別される char8_t
が欲しいってことですね。
すごく欲しいです。
英訳メモ(後で使うかも):
A type char8_t
is required for UTF-8 string literals, which is distinct from char
or any other integral types, to avoid encoding problems:
void f( char8_t const* utf8 ); f( u8"あいうえお" ); // OK. The string literal is encoded with UTF-8. f( "あいうえお" ); // should be ill-formed, // because it may be encoded with a encoding other than UTF-8.
一部のメタ関数を SFINAE に使えるようにしたい
現状、 std::result_of
や std::common_type
といった「型を取得するメタ関数」は、
与えられた型が前提条件を満たさない場合、クラステンプレートの内部でコンパイルエラーとなるため、 SFINAE に使うことが出来ません:
template< class F, class... Args > typename std::result_of<F(Args...)>::type invoke( F && f, Args&&... args ); struct X {}; void invoke( X const& ); int main() { invoke( X() ); // result_of の内部でコンパイルエラーになる }
勿論、これは decltype
(( std::common_type
の場合は条件演算子も)) を使って書き直せばいいのですが、
std::result_of
や std::common_type
の方が記述量が少なくて済むので、出来ればこっちを使いたいです。
規格を改めるとしたら、
前提条件を満たさない場合は nested type named type
は定義されない
的な文章を何処かに追加すればいい感じでしょうか。
その場合は std::common_type
の文面も変更する必要がありますね。
暗黙の move の適用範囲が狭すぎる
現行ドラフトでは、関数の戻り値と同じ型を持つローカル変数(および引数)を
return
文でそのまま返す場合、そのローカル変数は暗黙のうちに move されます:
struct Hoge {}; std::shared_ptr<Hoge> f() { std::shared_ptr<Hoge> p( new Hoge() ); return p; }
つまり、上記のコードにおいて、 p
は暗黙のうちに move されるので、
例え NRVO をサポートしていないコンパイラであっても、上記のコードにおいて、
参照カウントが無駄にインクリメント&デクリメントされる事はありません。
しかし、この暗黙の move は、暗黙の型変換が絡む場合には行われません。つまり、
struct Base {}; struct Derived : Base {}; std::shared_ptr<Base> f() { std::shared_ptr<Derived> p( new Derived() ); return p; }
上記の関数において、 p
が暗黙のうちに move されることはない、ということです。
といっても、 std::shared_ptr
程度なら、 move が copy になっても それほどコストが増えるわけではありません。
問題は、 std::list
のような、コピーコストの高いクラスから暗黙変換される場合で、
template< class T > struct wrapper { T value; wrapper( T && x ) : value( std::forward<T>(x) ) {} wrapper( T const& x ) : value( x ) {} }; wrapper<std::list<int>> f() { std::list<int> ls; // 大量に要素を追加 return ls; // 明らかに ls はこれ以上使われないのに、 move されない }
上記のようなコードは、要素数次第では無視できないコストがかかります。
といっても、このような場合ならば、明示的に return std::move(ls);
と書けばいい話です。
真に問題になるのは、コピーコストが大きいが、互換性の都合で未だ move に対応していない場合、
あるいは move においてもそれなりのコストがかかるような場合で、
その場合は return std::move(x);
と書くと NRVO が発動しないので、明示的に move しないケースよりコストが上乗せになってしまうのです。
この問題に、現行ドラフトの文面を変更しない形で対処するには、
コピーコストが大きくなる場合には、コピー自体を禁止したラッパークラスを作ってしまえばいい、ということが考えられます。
template< class T > struct noncopyable : T { template< class... Args, class = typename std::enable_if<std::is_constructible<T, Args...>::value>::type > explicit noncopyable( Args&&... args ) : T( std::forward<Args>(args)... ) {} noncopyable( noncopyable const& ) = delete; noncopyable( noncopyable && ) = default; noncopyable& operator=( noncopyable const& ) = delete; noncopyable& operator=( noncopyable && ) = default; }; noncopyable<std::list<int>> f() { noncopyable<std::list<int>> ls; // 大量に要素を追加 return ls; // もし暗黙に copy されるような場合にはコンパイルエラーとなる(今回は問題なし) }
一方で、このようなラッパーには限界も存在します。
std::list<int> f() // 戻り値を noncopyable 以外にすると { noncopyable<std::list<int>> ls; // 大量に要素を追加 return ls; // 暗黙にコピーされる、エラーにもならない }
なので、可能なら規格を改めて、
関数の return 文が return name ;
という形で、 name が参照以外のローカル変数または引数の場合には、
一律で暗黙の move が働くようにした方が良いのではないか、と思った次第。
* * *
と、このような些細な不満点はあるものの、
じゃあ再び FDIS を採択しよう、となると、8月の Bloomington meeting まで待たなければいけないので、
現実で妥協するのも仕方ない気がします。 今となっては、一刻も早く制定される方が良いなぁ、と。
ともあれ、ユーザ定義リテラルは滅ぶべきであると考える次第である。