Item 3の続きです。
以下のC++14の関数にまだ改善の余地があるというお話から。
universal referenceでrvalueをサポートする
template<typename Container, typename Index> decltype(auto) authAndAccess(Container& c, Index i);
この関数にはrvalueは渡せない。(Container&がconst参照でないため)
rvalueを渡すのはマイナーなケースだが、意味のある例は存在する
std::deque<std::string> makeStringDeque(); // dequeの5番目の要素のコピーを得る文 auto s = authAndAccess(makeStringDeque(), 5);
これをサポートするにはuniversal reference(詳細はItem 26)を使用すれば良い。
template<typename Container, typename Index> decltype(auto) authAndAccess(Container&& c, Index i);
なお、Indexは標準ライブラリの例(std::string, std::vector, std::dequeなど)が合理的だと思えるため値渡しにしている。
加えてItem27の勧告に従ってuniversal referenceにstd::forwardを適用する。
template<typename Container, typename Index> // C++14の最終版 decltype(auto) authAndAccess(Container& c, Index i) { authenticateUser(); return std::forward<Container>(c)[i]; } template<typename Container, typename Index> // C++11の最終版 auto authAndAccess(Container& c, Index i) -> decltype(std::forward<Container>(c)[i]) { authenticateUser(); return std::forward<Container>(c)[i]; }
decltypeの僅かな特殊なケース
decltypeは、変数名に関してはその変数の型を返すが、変数名以上に複雑なlvalueは全てlvalue referenceとして返す。
つまり、変数名以上に複雑な式のT型はT&型と型推定する。
しかしそのようなケースは少ない。(たとえば関数が返すlvalueはlvalue referenceである。さもなくばrvalue)
変数名以上に複雑なlvalueの例は、(x)だ。
int x = 0; // decltype(x)はint // decltype((x))はint& decltype(auto) f1() { int x = 0; ... return x; // f1の戻り値はint } decltype(auto) f2() { int x = 0; ... return (x); // f2の戻り値はint& }
f2の戻り値はローカル変数の参照なので、戻り値を受け取ったら未定義動作となってしまう!
注意点はあるが、通常の場合はdecltype(auto)は直感的な動作をする。
特に変数名に適用する場合は、decltypeという名前通り、変数の宣言された型(declared type)を返す。
Things to Remember
- decltypeはほぼ常に変数や式の型を修正無しに生成する
- 変数名より複雑なlvalue(たとえば"(x)")のTに対してdecltypeは常にT&を返す
- C++14はdecltype(auto)をサポートし、decltypeのルールに従って型推定する
0 件のコメント:
コメントを投稿