野良C++erの雑記帳 このページをアンテナに追加 RSSフィード Twitter

2011-04-20 「アンジェ、英語が全然ダメ子さんなので……」

FDIS の不満点

C++0x の FDIS について、細かい部分で幾つか不満な点が見つかったので、メモしておきます。

本来なら FDIS を採択した Madrid meeting 以前に言うべきであった事ばかりで、機を逃した感じですが、

気づいておきながら何も主張しないよりはマシだと思ったので。


std::basic_stringswapnoexcept 指定されていない

http://d.hatena.ne.jp/gintenlabo/20110413/1302689739 にも書いたとおり、

move ctor や move 代入演算子noexcept 指定されているので、 swapnoexcept 指定するべきです。


英訳メモ(後で使うかも):

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_tchar32_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_ofstd::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*1 を使って書き直せばいいのですが、

std::result_ofstd::common_type の方が記述量が少なくて済むので、出来ればこっちを使いたいです。


規格を改めるとしたら、

前提条件を満たさない場合は nested type named type は定義されない

的な文章を何処かに追加すればいい感じでしょうか。

その場合は std::common_type の文面も変更する必要がありますね。


暗黙の move の適用範囲が狭すぎる

参考: 銀天随筆集: C++0x における NRVO


現行ドラフトでは、関数の戻り値と同じ型を持つローカル変数(および引数)を

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 まで待たなければいけないので、

現実で妥協するのも仕方ない気がします。 今となっては、一刻も早く制定される方が良いなぁ、と。


ともあれ、ユーザ定義リテラルは滅ぶべきであると考える次第である。

*1std::common_type の場合は条件演算子

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/gintenlabo/20110420/1303288950