Effective Modern C++

【Effective Modern C++(1)】类型推断

2018-11-23  本文已影响35人  downdemo

01 理解模板类型推断

template<typename T>
void f(paramType param);
f(expr);
template<typename T>
void f(const T& param); // ParamType是const T&

int x = 0;
f(x); // T被推断为int,ParamType被推断为const int&

情形1:ParamType是指针/引用类型

template<typename T>
void f(T& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x); // T是int,ParamType是int&
f(cx); // T是const int,ParamType是const int&
f(rx); // T是const int,ParamType是const int&
template<typename T>
void f(const T& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x); // T是int,ParamType是const int&
f(cx); // T是int,ParamType是const int&
f(rx); // T是int,ParamType是const int&
template<typename T>
void f(const T* param);

int x = 27;
const int* px = &x;

f(&x); // T是int,ParamType是int*
f(px); // T是const int,ParamType是const int*

情形2:ParamType是转发引用

template<typename T>
void f(T&& param);

int x = 27;
const int cx = x;
const int& rx = x;
int&& rr = 27; // rr是右值引用,但也是左值

f(x); // T是int&,ParamType是int&
f(cx); // T是const int&,ParamType是const int&
f(rx); // T是const int&,ParamType是const int&
f(rx); // T是const int&,ParamType是const int&
f(27); // T是int,ParamType是int&&

情形3:ParamType不是引用或指针

template<typename T>
void f(T param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x); // T和ParamType都是int
f(cx); // T和ParamType都是int
f(rx); // T和ParamType都是int
template<typename T>
void f(T param);

const char* const ptr = "Fun with pointers";
const char name[] = "downdemo";
f(ptr); // T和ParamType都是const char* const
f(name); // T和ParamType都是const char*

特殊情形1:expr是数组名

template<typename T> void f1(T param);
template<typename T> void f2(T& param);
template<typename T> void f3(T&& param);

const char name[9] = "downdemo";
f1(name); // T和ParamType都是const char*
f2(name); // T是const char[9],ParamType是const char(&)[9],即const char(&param)[9]
f3(name); // 同上,T是const char[9],ParamType是const char(&)[9]
template <typename T, std::size_t N>
constexpr std::size_t arraySize(T (&)[N]) noexcept
{
    return N;
}
const char name[] = "downdemo";
int a[arraySize(name)]; // int a[9]

特殊情形2:expr是函数名

void someFunc(int, double);
template<typename T> void f1(T param);
template<typename T> void f2(T& param);
template<typename T> void f3(T&& param);

f1(someFunc); // 推断为ptr-to-fun,T和ParamType都是void(*)(int, double)
f2(someFunc); // 推断为ref-to-fun,T和ParamType都是void(&)(int, double)
f3(someFunc); // 同上,T和ParamType都是void(&)(int, double)

02 理解auto类型推断

auto x = 27;
const auto cx = x;
const auto& rx = x;

template<typename T> // 用来推断x类型的概念上假想的模板
void func_for_x(T param);
func_for_x(27); // 假想的调用: param的推断类型就是x的类型

template<typename T> // 用来推断cx类型的概念上假想的模板
void func_for_cx(const T param);
func_for_cx(x); // 假想的调用: param的推断类型就是cx的类型

template<typename T> // 用来推断rx类型的概念上假想的模板
void func_for_rx(const T& param);
func_for_rx(x); // 假想的调用: param的推断类型就是rx的类型
auto x = 27; // int x
const auto cx = x; // const int cx
const auto& rx = x; // const int& rx
auto&& uref1 = x; // int& uref1
auto&& uref2 = cx; // const int& uref2
auto&& uref3 = 27; // int&& uref3
const char name[] = "downdemo"; // 数组类型是const char[9]
auto arr1 = name; // const char* arr1
auto& arr2 = name; // const char (&arr2)[9]

void someFunc(int, double); // 函数类型是void(int, double)
auto func1 = someFunc; // void (*func1)(int, double)
auto& func2 = someFunc; // void (&func2)(int, double)
// C++98
int x1 = 27;
int x2(27);
// C++11
int x3 = { 27 };
int x4{ 27 };
auto x1 = 27; // int x1
auto x2(27); // int x2
auto x3 = { 27 }; // std::initializer_list<int> x3
auto x4{ 27 }; // C++11为std::initializer_list<int> x4,C++14为int x4
auto x5 = { 1, 2, 3.0 }; // 错误:不能为std::initializer_list<T>推断T
auto x1 = { 1, 2 }; // C++14中必须用=
auto x2 { 1 }; // 保留了单元素列表的直接初始化,但不会将其视为initializer_list
auto x = { 11, 23, 9 }; // x类型是std::initializer_list<int>

template<typename T> // 等价于x声明的模板
void f(T param);

f({ 11, 23, 9 }); // 错误:不能推断T的类型
template<typename T>
void f(std::initializer_list<T> initList);

f({ 11, 23, 9 }); // T被推断为int,initList类型是std::initializer_list<int>

C++14的auto

auto f() { return 1; }
auto g = [](auto x) {return x; };
auto newInitList() { return { 1 }; } // 错误
std::vector<int> v { 2, 4, 6};
auto resetV = [&v](const auto& newValue) { v = newValue; }; // C++14
resetV({ 1, 2, 3 }); // 错误

03 理解decltype

const int i = 0; // decltype(i)是const int

bool f(const Widget& w); // decltype(w)是const Widget&,decltype(f)是bool(const Widget&)

struct Point {
    int x, y; // decltype(Point::x)和decltype(Point::y)是int
};

Widget w; // decltype(w)是Widget
if (f(w)) … // decltype(f(w))是bool

int a[] {1, 2, 3}; // decltype(a)是int[3]

template<typename T> // std::vector的简化版
class vector {
public:
    …
    T& operator[](std::size_t index);
    …
};
vector<int> v; // decltype(v)是vector<int>
…
if (v[0] == 0) … // decltype(v[0])是int&
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i) -> decltype(c[i])
{
    authenticateUser(); // 验证用户有效
    return c[i];
}
template<typename Container, typename Index>
auto authAndAccess(Container& c, Index i)
{
    authenticateUser(); // 验证用户有效
    return c[i];
}
std::deque<int> d;
…
authAndAccess(d, 5) = 10; // 返回d[5]然后赋值为10,但不能通过编译
template<typename Container, typename Index>
decltype(auto)
authAndAccess(Container& c, Index i)
{
    authenticateUser();
    return c[i];
}
Widget w;
const Widget& cw = w;
auto myWidget1 = cw; // auto类型推断:myWidget1类型是Widget
decltype(auto) myWidget2 = cw; // decltype类型推断:myWidget2类型是const Widget&
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container& c, Index i);
std::deque<std::string> makeStringDeque(); // 工厂函数
// 拷贝从makeStringDeque返回的第五个元素
auto s = authAndAccess(makeStringDeque(), 5);
// 最终的C++14版本,c现在是一个转发引用
template<typename Container, typename Index>
decltype(auto) authAndAccess(Container&& c, Index i)
{
    authenticateUser();
    return std::forward<Container>(c)[i];
}
// 最终的C++11版本,将decltype(auto)改为auto结合尾置返回类型
template<typename Container, typename Index>
auto
authAndAccess(Container&& c, Index i) // version
-> decltype(std::forward<Container>(c)[i])
{
    authenticateUser();
    return std::forward<Container>(c)[i];
}

decltype的特殊情况

int* p; // decltype(*p)是int&
int i = 0; // decltype(i=1)是int&
int n = 4;
decltype(i=1) r = n;
r = 5;
std::cout << i << n << r; // 055
int i; // decltype((i))是int&
decltype(auto) f1()
{
    int x = 0;
    …
    return x; // decltype(x)是int,因此返回int
}
decltype(auto) f2()
{
    int x = 0;
    …
    return (x); // decltype((x))是int&,因此返回了局部变量的引用
}

04 查看推断类型的方法

鼠标放在x上
template<typename T> class TD;

TD<decltype(x)> xType; // 未定义类模板,错误信息将提示x类型
// 比如对int x报错如下
error C2079: “xType”使用未定义的 class“TD<int>”
template<typename T>
void f(T& param)
{
    using std::cout;
    cout << "T = " << typeid(T).name() << '\n';
    cout << "param = " << typeid(param).name() << '\n';
}
#include <boost/type_index.hpp>

template<typename T>
void f(const T& param)
{
    using std::cout;
    using boost::typeindex::type_id_with_cvr;
    cout << "T = " << type_id_with_cvr<T>().pretty_name() << '\n';
    cout << "param = " << type_id_with_cvr<decltype(param)>().pretty_name() << '\n';
}
上一篇下一篇

猜你喜欢

热点阅读