Post on 17-Jul-2015
decltypeの基本的なルール
• decltypeは通常、名前や式で与えたものの型を返す
3
const int i = 0; // decltype(i) is const int.
bool f(const Widget& w); // decltype(w) is const Widget&,
// decltype(f) is
// bool(const Widget&)
struct Point {int x, y;}; // decltype(Point::x) is int.
Widget w; // decltype(w) is Widget.
f(w); // decltype(f(w)) is bool.
コンテナのoperator[]が返す型
•コンテナのoperator[]は通常T&を返すが、std::vector<bool>はbool&を返さない
4
template <typename T>
struct my_vector {
T& operator[](size_t i);
};
my_vector<int>v; // decltype(v) is my_vector<int>.
v[0]; // decltype(v[0]) is int&.
std::vector<bool> w; // decltype(w) is std::vector<bool>.
w[0]; // decltype(w[0]) is
// std::_Bit_reference&.
// (using clang++)
operator[]の返す型を返す関数
• C++11:単文ラムダのみ返り値の型を推論
• C++14:任意のラムダ、関数で推論
5
// C++11
template <typename Container, typename Index>
auto authAndAccess(Container& c, Index i) -> decltype(c[i])
{
return c[i];
}
// C++14
template <typename Container, typename Index>
auto authAndAccess(Container& c, Index i) {
return c[i];
} これだと decltype(c[i]) 型にならない
autoとdecltype(auto)
• autoの部分の型を決定するときに参照情報は無視される
6
//C++14
template <typename Container, typename Index>
auto authAndAccess(Container& c, Index i) {
return c[i];
}
std::deque<int> d;
authAndAccess(d, 5) = 10;
// compile error
// decltype(authAndAccess(d, 5)) is int.
autoとdecltype(auto) –cont.
• decltype(auto)を使えば参照情報は無視されない
7
//C++14
template <typename Container, typename Index>
decltype(auto) authAndAccess(Container& c, Index i) {
return c[i];
}
std::deque<int> d;
authAndAccess(d, 5) = 10;
// decltype(authAndAccess(d, 5)) is int&.
変数宣言でのdecltype(auto)
• autoで無視される参照情報やconst/volatileがdecltype(auto)では無視されない
8
Widget w;
const Widget& cw = w;
auto myWidget1 = cw; // Widget.
decltype(auto) myWidget2 = cw; // const Widget&.
rvalueを渡せない
• constでないlvalue referenceで受ける引数にrvalueは渡せない
9
template <typename Container, typename Index>
decltype(auto) authAndAccess(Container& c, Index i);
std::deque<std::string> makeStringDeque();
auto s = authAndAccess(makeStringDeque(), 5);
// compile error
Universal referenceを使う
• Perfect forwarding
10
//C++14
template <typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index i) {
return std::forward<Container>(c)[i];
}
//C++11
template <typename Container, typename Index>
auto authAndAccess(Container&& c, Index i) ->
decltype(std::forward<Container>c[i]);
std::deque<std::string> makeStringDeque();
auto s = authAndAccess(makeStringDequeu(), 5);
decltypeの特殊ルール
• decltype(lvalue expression)は必ずlvalue referenceになる
11
int x = 0;
decltype(x) y1;
// x is name, so y1’s type is int.
decltype((x)) y2;
// (x) is lvalue expression, so y2’s type is int&.
decltypeの特殊ルール –cont.
•関数の返り値型の推論も同様
12
decltype(auto) f1() {
int x = 0;
return x; // int
}
decltype(auto) f2() {
int x = 0;
return (x); // int&
} ローカル変数の参照を返してしまっている
Item 3 Things to Remember
• decltypeは大体は変数や式の型になる
•型Tの名前以外のlvalue expressionについてのdecltypeは型T&になる.
• C++14はdecltype(auto)をサポートし、auto同様に初期化子から推論するが、decltypeのルールに従う
13
IDEを使う –cont.
•複雑な型だと分かりづらい
17
template <typename T>
void f(const T& param) {}
struct Widget {};
std::vector<Widget> createVec() { return {{}}; }
int main(){
const auto v = createVec();
f(&v[0]);
// void f<const std::_Simple_types<std::_Wrap_alloc<
// std::_Vec_base_types<Widget, std::allocator<Widget> >
// ::_Alloc>::value_type>::value_type *>(
// const std::_Simple_types<...>::value_type
// *const ¶m)
} vc2013だとこう表示される
コンパイラを使う
18
template <typename T>
class TD; // TD is Type Displayer.
const int x = 0;
auto y = x;
auto z = &x;
TD<decltype(y)> yType;
TD<decltype(z)> zType;
> clang++ -std=c++1y t.cpp
t.cpp:11:21: error: implicit instantiation of undefined
template 'TD<int>‘
...
t.cpp:12:21: error: implicit instantiation of undefined
template 'TD<const int *>‘
...
コンパイラを使う –cont.
19
std::map<std::string, std::vector<int>> m;
const auto v = m;
TD<decltype(m)> mType;
> clang++ -std=c++1y t.cpp
...
t.cpp:16:21: error: implicit instantiation of undefined
template
'TD<std::map<std::basic_string<char>, std::vector<int,
std::allocator<int> >,
std::less<std::basic_string<char> >,
std::allocator<std::pair<const std::basic_string<char>,
std::vector<int, std::allocator<int> > > > > >'
TD<decltype(m)> mType;
...
typeidを使う
• i: int
• P: pointer
• K: const
20
const int x = 0;
auto y = x;
auto z = &x;
std::cout << typeid(y).name() << std::endl;
std::cout << typeid(z).name() << std::endl;
> clang++ -sd=c++1y t.cpp && ./a.out
i
PKi
typeidを使う –cont.
21
struct Widget {};
std::vector<Widget> createVec() { return {{}}; }
template <typename T>
void f(const T& param){
std::cout << typeid(T).name() << std::endl;
std::cout << typeid(param).name() << std::endl;
}
const auto v = createVec();
f(&v[0]);
> clang++ -sd=c++1y t.cpp && ./a.out
PK6Widget // 正しいPK6Widget // 誤 const Widget *
// 正 const Widget * const &
Boost TypeIndexを使う
22
#include <iostream>
#include <vector>
#include <boost/type_index.hpp>
template <typename T>
void f(const T& param) {
using boost::typeindex::type_id_with_cvr;
std::cout
<< type_id_with_cvr<T>().pretty_name() << std::endl
<< type_id_with_cvr<decltype(param)>().pretty_name()
<< std::endl;
}
struct Widget {};
std::vector<Widget> createVec() { return {{}}; }
int main() {
const auto v = createVec();
f(&v[0]);
}
Boost TypeIndexを使う –cont.
• TypeIndexはBoost 1.56から使える
23
> clang++ -std=c++1y t.cpp && ./a.out
Widget const*
Widget const* const&