2011年12月17日土曜日

【C++11 Advent Calendar 2011】17日目 小ネタ集 前編

本記事は C++11 Advent Calendar 17日目の記事です。
C++er 待望の C++11、大きな変更が目白押しでどうしてもその辺りが注目されるわけですがそういうのは偉い先生に任せて今回は軽く小ネタ集で行ってみましょう。
ちなみに私が持っているのは n3291 ですので最終的な規格と違っている可能性があります、悪しからずご了承ください。
さて、発行されたばかりの C++11、書籍もまだ出ていない(と思う)訳ですが、Effective C++ 等で有名な Scott Meyers 先生が C++11 解説コース用の資料を PDF で販売されています(Presentation Materials: Overview of the New C++ (C++11))。前編ではこれをさらっと流し読みした中で、へぇぇと思った点を挙げていきたいと思います。なお、ページ数は cpp11Notes2011-10-25 でのものです。

■ auto と template parameter type deduction (p.22)

auto による型導出と template parameter の型導出とは基本的に同一となりますが
template<typename T> void f(T t);

// 基本的に一緒
f(expr);
auto v = expr;
唯一違う(と Meyers 先生が言っている)のが expr に brace initialization list、つまり { 1, 2, 3 } などが与えられた場合です。auto は std::initializer_list<T> (この場合 T = int)となりますが、template parameter の場合は型導出されません。
gcc 4.5/4.6 の場合はデフォルトでは警告になりますが、4.7 だとエラーになります。

■文字リテラル、文字列リテラルと、UCS-2 / UTF-16 (p.34, 35)

u'x' の場合 x は UCS-2 で表現可能な範囲内(つまり BMP 内)である必要がありますが、u"hoge" の場合、hoge は UTF-16 で表現可能な範囲内(つまり Unicode 全域)でも OK です。

■Implicit narrowing (p.52-)

C++11 で地味に便利な機能として Uniform Initialization があります。簡単に言ってしまえば今まで配列、(コンストラクタに依らない)構造体の初期化は int a[3] = { 1, 2, 3 } のような形式で、クラスの初期化は X x(arg); のような形式で統一されていませんでした。これが統一的に
int a[3] { 1, 2, 3 }; // = 省略可能

X x{arg};
と書けるようになるのですが、一点注意しなければならないのがこの形式の場合 Implicit narrowing を認めないという点です。
struct Point { int x, y; }
Point p1 = { 1, 2.5 }; // 構文的には C++11 では = なくてもよい
これが C++11 で通りません。まぁ、これはまだ感覚的に納得できそうですが実はこれも通りません。
// gcc 4.5 / 4.6 だと通ってしまうが
// gcc 4.7 / clang 3.0 だとエラー
int x = 1;
double d{x};
ただしこれなら通ります。
const int x = 1;
double d{x};
一方(通常の環境では)これは通りません。
const int x = 0x7FFFFFFF;
float d{x}; // float で表現できない
認められるケースは constant expression でかつ変換先で表現可能な範囲の場合に限定されているんですね。なお、元ネタ資料では error: double can’t exactly represent all ints ってコメントが付いてますが表現可能なんじゃないでしょうか。


■const T&& (p.245) / delete (p.294)

「const T&& は合法だけど使い道なんかねーよ」「既に発見されたけどな!(正にC++er)」(意訳)
規格中でも使われています。
template <class T> reference_wrapper<T> ref(T&) noexcept;
template <class T> void ref(const T&&) = delete;
lvalue reference に対して呼び出せるけど rvalue reference に対して呼び出せないようにするためのものです。これが有効なのは deleted な関数は使えないけど宣言はされているためです。従ってコピーコンストラクタを delete した場合、暗黙のコンストラクタ生成等も抑止され以下のコードはコンパイルできません(暗黙のデフォルトコンストラクタがない)。
struct A
{
    A(const A&) = delete;
};
A a;

■explicit operator bool (p.312)

コンストラクタへの explicit 指定は皆さん使っていますね? C++11 では変換演算子に対しても explicit 指定ができるようになりました。が、explicit operator bool だけは特殊で「文脈的に bool に変換されている場合(contextually converted to bool)」明示的な変換を必要としません。unspecified bool type idiom の置き換えを狙ったものでしょう。
struct A { explicit operator bool() const { return false; } } a;
// OK
if(a) { /**/ }         // if, while, do-while, for の条件
int m = !a;            // ! 演算子の引数
int n = a ? 1 : 0;     // 条件演算子の引数
if(a || true) { /**/ } // ||, && 演算子の引数
// 後は static_assert や noexcept 修飾子の引数も暗黙変換可能

// BAD
int o = a; // これは駄目
Effective C++11 とか Exceptional C++11 とかが欲しいところですね。さて、書いてみるとこれだけだとネタが薄いかな(しかも他人のふんどし)ということで、後編では C99 から C++11 に取り込まれた点を見てみます。

0 件のコメント:

コメントを投稿