Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CPlusPlusNotesForProfessionals.pdf
Скачиваний:
47
Добавлен:
20.05.2023
Размер:
5.11 Mб
Скачать

Chapter 98: type deduction

Section 98.1: Template parameter deduction for constructors

Prior to C++17, template deduction cannot deduce the class type for you in a constructor. It must be explicitly specified. Sometimes, however, these types can be very cumbersome or (in the case of lambdas) impossible to name, so we got a proliferation of type factories (like make_pair(), make_tuple(), back_inserter(), etc.).

Version ≥ C++17

This is no longer necessary:

std::pair p(2, 4.5);

// std::pair<int, double>

std::tuple t(4, 3, 2.5); //

std::tuple<int, int, double>

std::copy_n(vi1.begin(), 3,

 

std::back_insert_iterator(vi2)); // constructs a back_insert_iterator<std::vector<int>> std::lock_guard lk(mtx); // std::lock_guard<decltype(mtx)>

Constructors are considered to deduce the class template parameters, but in some cases this is insu cient and we can provide explicit deduction guides:

template <class Iter>

vector(Iter, Iter) -> vector<typename iterator_traits<Iter>::value_type>

int array[] = {1, 2, 3};

std::vector v(std::begin(array), std::end(array)); // deduces std::vector<int>

Section 98.2: Auto Type Deduction

Version ≥ C++11

Type deduction using the auto keyword works almost the same as Template Type Deduction. Below are a few examples:

auto x = 27;

// (x is neither a pointer nor a reference), x's type is int

const auto cx = x;

// (cx is neither a pointer nor a reference), cs's type is const int

const auto& rx = x;

// (rx is a non-universal reference), rx's type is a reference to a const

int

 

auto&& uref1 = x;

// x is int and lvalue, so uref1's type is int&

auto&& uref2 = cx;

// cx is const int and lvalue, so uref2's type is const int &

auto&& uref3 = 27;

// 27 is an int and rvalue, so uref3's type is int&&

 

 

The di erences are outlined below:

auto x1 = 27; // type is int, value is 27 auto x2(27); // type is int, value is 27

auto x3 = { 27 }; // type is std::initializer_list<int>, value is { 27 } auto x4{ 27 }; // type is std::initializer_list<int>, value is { 27 }

//in some compilers type may be deduced as an int with a

//value of 27. See remarks for more information.

auto x5 = { 1, 2.0 } // error! can't deduce T for std::initializer_list<t>

As you can see if you use braced initializers, auto is forced into creating a variable of type std::initializer_list<T>. If it can't deduce the of T, the code is rejected.

GoalKicker.com – C++ Notes for Professionals

505

When auto is used as the return type of a function, it specifies that the function has a trailing return type.

auto f() -> int { return 42;

}

Version ≥ C++14

C++14 allows, in addition to the usages of auto allowed in C++11, the following:

1.When used as the return type of a function without a trailing return type, specifies that the function's return type should be deduced from the return statements in the function's body, if any.

// f returns int:

auto f() { return 42; } // g returns void:

auto g() { std::cout << "hello, world!\n"; }

2. When used in the parameter type of a lambda, defines the lambda to be a generic lambda.

auto triple = [](auto x) { return 3*x; };

const auto x = triple(42); // x is a const int with value 126

The special form decltype(auto) deduces a type using the type deduction rules of decltype rather than those of auto.

int* p

= new

int(42);

 

auto x

= *p;

// x

has type int

decltype(auto) y = *p; // y

is a reference to *p

 

 

 

 

In C++03 and earlier, the auto keyword had a completely di erent meaning as a storage class specifier that was inherited from C.

Section 98.3: Template Type Deduction

Template Generic Syntax

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

f(expr);

Case 1: ParamType is a Reference or Pointer, but not a Universal or Forward Reference. In this case type deduction works this way. The compiler ignores the reference part if it exists in expr. The compiler then pattern-matches expr's type against ParamType to determing T.

template<typename T>

 

void f(T& param);

//param is a reference

int x = 27;

// x is an int

const int cx = x;

// cx is a const int

const int& rx = x;

// rx is a reference to x as a const int

f(x);

// T is int, param's type is int&

f(cx);

// T is const int, param's type is const int&

f(rx);

// T is const int, param's type is const int&

 

 

GoalKicker.com – C++ Notes for Professionals

506

Case 2: ParamType is a Universal Reference or Forward Reference. In this case type deduction is the same as in case 1 if the expr is an rvalue. If expr is an lvalue, both T and ParamType are deduced to be lvalue references.

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

int x = 27; const int cx = x;

const int& rx = x;

f(x); f(cx); f(rx); f(27);

//param is a universal reference

//x is an int

//cx is a const int

//rx is a reference to x as a const int

//x is lvalue, so T is int&, param's type is also int&

//cx is lvalue, so T is const int&, param's type is also const int&

//rx is lvalue, so T is const int&, param's type is also const int&

//27 is rvalue, so T is int, param's type is therefore int&&

Case 3: ParamType is Neither a Pointer nor a Reference. If expr is a reference the reference part is ignored. If expr is const that is ignored as well. If it is volatile that is also ignored when deducing T's type.

template<typename T>

 

void f(T param);

// param is now passed by value

int x = 27;

// x is an int

const int cx = x;

// cx is a const int

const int& rx = x;

// rx is a reference to x as a const int

f(x);

// T's and param's types are both int

f(cx);

// T's and param's types are again both int

f(rx);

// T's and param's types are still both int

 

 

GoalKicker.com – C++ Notes for Professionals

507