30/09/2018, 19:24

[Solved] Ý nghĩa và cách sử dụng type deduction và auto trong C++11/14

Mình mới tìm hiểu về C++11/14 thì gặp vấn đề type deduction và auto. Mình nắm được cách sử dụng của auto khi khai báo biến và hàm trả về sử dụng auto. Nhưng để hiểu sau và vận dụng tốt thì phải nắm rõ cơ chế của type deduction. Có cao nhân nào đã sử dụng và hiểu bản chất thì chỉ dạy mình với. Mình có đọc ở quyển Effective Modern C++ nhưng còn mù mịt qúa

Daniel viết 21:32 ngày 30/09/2018

Hôm qua mình có ngồi đọc lại và có note lại một số ý sau về template, thực sự là khá lằng nhằng

template

void f(ParamType param);

f(expr);

Khi compile, compiler sẽ dùng expr để deduce (suy luận) hai kiểu T và ParamType.

Type của T không chỉ phụ thuộc vào type của expr và còn phụ thuộc vào form của ParamType. Có ba trường hợp:

  • ParamType là một pointer hoặc reference, nhưng không phải là universal reference

  • ParamType là một universal reference

  • ParamType không phải pointer hoặc reference (kiểu pass-by-value)

Case 1: ParamType là reference hoặc pointer

Trong trường hợp này, type deduction hoạt động như sau:

  • Nếu expr là reference thì bỏ qua phần reference này.

  • Dựa vào kiểu của expr và ParamType để xác định T.

Ví dụ:

template

void f(T& param);

int x = 27;

cons tint cx = x;

const int& rx = x;

f(x); //T là int, kiểu của param là int&

f(cx); //T là const int, kiểu của param là const int&

f(rx); //T là const int mặc dù expr là int& do bỏ qua reference

//Kiểu của param là const int&

Ở f(cx) và f(rx) parameter là một const, khi deduce thì constness của nó
vẫn giữ nguyên và được deduce cho T. Khác với case 3 khi ParamType
không phải là pointer hay reference, ở case 3 constness và
reference-ness bị bỏ qua khi deduce.

Nếu đổi kiểu tham số truyền vào của f từ T& sang const T& thì
constness của cx và rx vẫn được giữ nguyên, ở x khi deduce sẽ là const
vì ParamType là const T&. Nghĩa là không cần quan tâm đến T có là
const hay không, khi deduce thì param đều có kiểu là reference-to-const.

template

void f(const T& param);

int x = 27;

cons tint cx = x;

const int& rx = x;

f(x); //T là int, kiểu của param là const int&

f(cx); //T là int, kiểu của param là const int&

f(rx); //T là int mặc dù expr là int& do bỏ qua reference

//Kiểu của param là const int&

Với pointer cũng tương tự:

template

void f(T* param);

int x = 27;

const int *px = &x;

f(&x); //T là int, kiểu của param là int*

f(px); //T là const int, kiểu của param là const int*

Case 2: ParamType là universal reference

template

void f(T& param);

Parameter được khai báo giống như rvalue reference (T&&) nhưng
sẽ hoạt động khác khi lvalue argument được truyền vào. Trong trường hợp
nào type deduction hoạt động như sau:

  • Nếu expr là một lvalue, cả T và ParamType đều được deduce là lvalue reference.

  • Nếu expr là một rvalue, thì T và ParamType được deduce bình thường.

Ví dụ:

template

void f(T&& param);

int x = 27;

const int cx = x;

const int& rx = x;

f(x); //x là lvalue, T là int&, kiểu của param là int&

f(cx); //cx là lvalue, T là int&, kiểu của param là const int&

f(rx); //rx là lvalue, T là const int&

//kiểu của param là const int&

f(27); //27 là rvalue, T là int, kiểu của param là int&&

Case 3: ParamType không phải là pointer hay reference

template

void f(T param);

Trong trường hợp này param sẽ copy expr truyền vào vào một object mới. Type deduction hoạt động như sau:

  • Nếu expr là reference thì bỏ qua phần reference.

  • Sau khi ignore reference-ness, nếu expr là constness cũng bị ignore.

Ví dụ:

template

void f(T param);

int x = 27;

cons tint cx = x;

const int& rx = x;

f(x); //T là int, kiểu của param là int

f(cx); //T là int, kiểu của param là int

f(rx); //T là int, kiểu của param là int

Trong trường hợp này mặc dù cx và rx đều là const nhưng param không phải
là const vì param là một object hoàn toàn độc lập với cx và rx, nó là
một bản copy của cx và rx, param không thể thay đổi được cx và rx. Đó
cũng là lý do vì sao constness của expr được ignore.

Ví dụ:

template

void f(const T param);

int x = 27;

const int cx = x;

const int& rx = x;

f(x); //T là int, kiểu của param là const int

f(cx); //T là int, kiểu của param là const int

f(rx); //T là int, kiểu của param là const int

Nếu expr là một const pointer đến một const object

template

void f(T param); // param passed by value

const char* const ptr =“Fun with pointers”; // ptr là const pointer đến một const object

f(ptr);

viết 21:32 ngày 30/09/2018

cho code và giữa 3 dấu ``` hêt đi:sweat_smile:
```
code
```

phần template khó, trừ phi bạn muốn viết code làm thư viện cho người khác sử dụng chứ ko phải viết code cho mình sử dụng thì mới đọc

Daniel viết 21:33 ngày 30/09/2018

Okie, mình sẽ sửa sau, phần này mình muốn nắm rõ bản chất để sau có thể tối ưu hiệu năng. Cho mình hỏi thêm chút về initializer_list, mình chưa có khái niệm gì về cái này

viết 21:38 ngày 30/09/2018

nó tương tự như std::vector thôi.
trong C++0x thì ko viết được thế này:
std::vector<int> p = { 1, 2, 3, 4 };
nhưng lại viết được thế này:
int p[] = {1, 2, 3 , 4 };
là 1 bất tiện rất rất lớn nếu bạn khuyên người khác sử dụng std::vector thế cho C array. Vì vậy C++11 chế ra cái initializer_list để viết std::vector<int> p = { 1, 2, 3, 4 }; được. {1,2,3,4} là 1 initializer_list, bạn cứ đối xử với nó như std::vector vậy. (đúng hơn là const std::vector)

http://en.cppreference.com/w/cpp/utility/initializer_list

nó chỉ có 3 phương thức là size(), begin(), và end(), ko có operator[], nhưng cũng tạm đủ để coi nó như mảng thông thường rồi.

Daniel viết 21:36 ngày 30/09/2018

Cảm ơn bạn đã trả lời. Khả năng đọc tiếng anh của mình khá chậm nên đôi khi vội thì ngaị đọc
Trong C++11 vẫn viết được std::vector chứ nhỉ?

viết 21:25 ngày 30/09/2018

vẫn được mà @_@

initializer_list có thể xem như là 1 cái array tạm. std::vector thì phải khai báo rồi khởi tạo. Có initializer_list thì cũng có thể tạo 1 vector tạm thời được rồi
ví dụ

int sum(const std::vector<int>& v)
{
    return std::accumulate(v.begin(), v.end(), 0);
}

int main()
{
    std::cout << sum( {1,2,3,4} ) << "\n";
}
Daniel viết 21:34 ngày 30/09/2018

std::vector p = { 1, 2, 3, 4 };
Mình viết thiếu, ý mình là như trên cũng vẫn okie ý. Vì dùng được như thế nên mình vẫn băn khoăn sự khác biệt của vector hay array với std::initializer_list

viết 21:40 ngày 30/09/2018

được, C++11 tự động chuyển cái initializer_list về vector. Đúng hơn là vector có copy ctor cho initializer_list.

ví dụ

std::string s = "1234";
std::vector<int> p = {1,2,3,4}; //chỉ viết được trong C++11/14, C++98/0x ko viết được thế này
```
có thể xem `{1,2,3,4}` như là 1 literal như `"1234"` vậy
Daniel viết 21:40 ngày 30/09/2018

http://www.bogotobogo.com/cplusplus/C11/C11_initializer_list.php
Bài viết này khá dễ hiểu, tóm lại là cho phép constructor và function sử dụng initializer_líst như một parameter, với vector thì không dùng được như thế, phải khởi tạo và gán gía trị cho vector rồi mới truyền vào trong hàm được.
Gio mình mới hiểu ý bạn. Bạn cao thủ qúa (y)

Bài liên quan
0