CS计算机代考程序代写 c++ COMP6771 Advanced C++ Programming

COMP6771 Advanced C++ Programming
Week 8.2 Advanced Types
1

decltype
decltype(e)
Semantic equivalent of a “typeof” function for C++
Rule 1:
If expression e is any of:
variable in local scope variable in namespace scope static member variable function parameters
then result is variable/parameters type T
Rule 2: if e is an lvalue (i.e. reference), result is T& Rule 3: if e is an xvalue, result is T&&
Rule 4: if e is a prvalue, result is T
xvalue/prvalue are forms of rvalues. We do not require you to know this. Non-simplified set of rules can be found here.
2.1

decltype
Examples include:
1 int i;
2 int j& = i;
3
4 decltype(i) x; // int – variable
5 decltype((j)) y; // int& – lvalue
6 decltype(5) z; // int – prvalue
2.2

Determining return types
Iterator used over templated collection and returns a reference to an item at a particular index
1
2
3
4
5 6} 7}
template
??? find(It beg, It end, int index) {
for (auto it = beg, int i = 0; beg != end; ++it; ++i) {
if (i == index) {
return *it;
8 return end; 9}
We know the return type should be decltype(*beg), since we know the type of what is returned is of type *beg
2.3

Determining return types
This will not work, as beg is not declared until after the reference to beg
template
decltype(*beg) find(It beg, It end, int index) {
1
2
3
4
5 6} 7}
for (auto it = beg, int i = 0; beg != end; ++it; ++i) {
if (i == index) {
8 return end; 9}
return *it;
Introduction of C++11 Trailing Return Types solves this problem for us
template
auto find(It beg, It end, int index) -> decltype(*beg) {
1
2
3
4
5 6} 7}
for (auto it = beg, int i = 0; beg != end; ++it, ++i) {
if (i == index) {
8 return end; 9}
return *it;
2.4

Type Transformations
A number of add, remove, and make functions exist as part of type traits that provide an ability to transform types
3.1

Type Transformations
1 #include
2 #include
3
4 template
5 auto print_is_same() -> void {
6
7}
8
9 auto main() -> int {
std::cout << std::is_same() << "\n"; 10 std::cout << std::boolalpha; 11 print_is_same();
12 // true
13 print_is_same(); // false
14 print_is_same(); // false
15 print_is_same::type>();
16 // true
17 print_is_same::type>(); // true
18 print_is_same::type>(); // true
19 print_is_same::type>(); // true
20 }
demo850-transform.cpp
3.2

Type Transformations
1 #include
2 #include
3
4 auto main() -> int {
5 using A = std::add_rvalue_reference::type;
6 using B = std::add_rvalue_reference::type;
7 using C = std::add_rvalue_reference::type;
8 using D = std::add_rvalue_reference::type;
9
10 std::cout << std::boolalpha 11 std::cout << "typedefs of int&&:" << "\n"; 12 std::cout << "A: " << std::is_same>::value << "\n"; 13 std::cout << "B: " << std::is_same>::value << "\n"; 14 std::cout << "C: " << std::is_same>::value << "\n"; 15 std::cout << "D: " << std::is_same>::value << "\n"; 16 } 3.3 Shortened Type Trait Names Since C++14/C++17 you can use shortened type trait names. 1 #include
2 #include
3
4 auto main() -> int {
5 using A = std::add_rvalue_reference;
6 using B = std::add_rvalue_reference;
7
8 std::cout << std::boolalpha 9 std::cout << "typedefs of int&&:" << "\n"; 10 std::cout << "A: " << std::is_same>::value << "\n"; 11 std::cout << "B: " << std::is_same>::value << "\n"; 12 } 3.4 Parameters Binding Arguments lvalue const lvalue rvalue const rvalue template T&& Yes Yes Yes Yes T& Yes const T& Yes Yes Yes Yes T&& Yes Note: const T& binds to everything! template T&& can by binded to by everything! template void foo(T&& a);
4.1

Examples
#include
auto print(std::string const& a) -> void {
std::cout << a << "\n"; 7 auto goo() -> std::string const { 8 return “C++”;
9}
10
11 auto main() -> int {
12 auto j = std::string{“C++”};
13 auto const& k = “C++”;
14 print(“C++”); // rvalue
15 print(goo()); // rvalue
16 print(j); // lvalue
17 print(k); // const lvalue
18 }
1
2
3
4 5} 6
1 #include 2
3 template
4 auto print(T&& a) ->
void {
a << "\n"; 5 6} 7 8 auto goo() -> std::string const {
9 return “Test”;
std::cout << 10 } 11 12 auto main() -> int {
13 auto j = int{1};
14 auto const& k = 1;
15
16 print(1); // rvalue,
17 print(goo()); // rvalue
18 print(j); // lvalue
19 print(k); // const lvalue foo(const int&)
20 }
foo(int&&)
foo(const int&&)
foo(int&)
demo851-bind1.cpp
demo852-bind2.cpp
4.2

Forwarding references
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a forwarding reference (AKA universal reference in some older texts).
1 int n;
2 int& lvalue = n; // Lvalue reference
3 int&& rvalue = std::move(n); // Rvalue reference
4
5 template T&& universal = n; // This is a universal reference.
6 auto&& universal_auto = n; // This is the same as the above line.
7
8 template
9 void f(T&& param); // Universal reference
10
11 template
12 void f(std::vector&& param); // Rvalue reference (read the rules carefully).
For more details on forwarding references, see this blog post
4.3

Forwarding functions
Attempt 1: Take in a value What’s wrong with this?
1 template 2 auto wrapper(T value) { 3 return fn(value); 4}
What if we pass in a non-copyable type? What happens if we pass in a type that’s expensive to copy
5.1

Forwarding functions
Attempt 2: Take in a const reference What’s wrong with this?
1 template
2 auto wrapper(T const& value) { 3 return fn(value);
4}
What happens if wrapper needs to modify value?
Code fails to compile
What happens if we pass in an rvalue?
1 // Calls fn(x)
2 // Should call fn(std::move(x)) 3 wrapper(std::move(x));
5.2

Forwarding functions
Attempt 3: Take in a mutable reference What’s wrong with this?
1 template
2 auto wrapper(T& value) { 3 return fn(value);
4}
What happens if we pass in a const object? What happens if we pass in an rvalue?
1 constintn=1; 2 wrapper(n);
3 wrapper(1)
5.3

Interlude: Reference collapsing
An rvalue reference to an rvalue reference becomes (¡°collapses into¡±) an rvalue reference.
All other references to references (i.e., all combinations involving an lvalue reference) collapse into an lvalue reference.
T& & -> T& T&& & -> T& T& && -> T& T&& && -> T&&
5.4

Forwarding functions
Attempt 4: Forwarding references What’s wrong with this?
1 // Instantiation generated
2 template <>
3 auto wrapper((int&)&& value) {
4 5} 6
7 // Collapses to
8 template <>
9 auto wrapper(int& value) {
10 return fn(value);
11 }
12
13 int i;
14 wrapper(i);
return fn(value);
1
2
3 4} 5
6
7
8 9}
// Instantiation generated
auto wrapper((int&&)&& value) {
return fn(value);
// Collapses to
auto wrapper(int&& value) {
return fn(value);
10
11 int i;
12 wrapper(std::move(i));
Calls fn(i)
Also calls fn(i)
The parameter is an rvalue, but inside the function, value is an lvalue
1 template
2 auto wrapper(T&& value) { 3 return fn(value);
4}
5.5

Forwarding functions
Attempt 4: Forwarding references We want to generate this
1 // We want to generate this.
2 auto wrapper(int& value) {
3 return fn(static_cast(value)); 4}
1 // We want to generate this
2 auto wrapper(int&& value) {
3 return fn(static_cast(value)); 4}
It turns out there’s a function for this already
1
2
3
4
5 6}
template
auto wrapper(T&& value) {
return fn(std::forward(value));
// Equivelantly (don’t do this, forward is easier to read).
return fn(static_cast(value));
5.6

std::forward and variadic templates
Often you need to call a function you know nothing about
It may have any amount of parameters
Each parameter may be a different unknown type Each parameter may be an lvalue or rvalue
1 template
2 auto make_unique(Args&&… args) -> std::unique_ptr {
3
4
5
6
7
8 9}
// Note that the … is outside the forward call, and not right next to args.
// This is because we want to call
// new T(forward(arg1), forward(arg2), …)
// and not
// new T(forward(arg1, arg2, …))
return std::unique_ptr(new T(std::forward(args)…));
5.7

uses of std::forward
The only real use for std::forward is when you want to wrap a function with a parameterized type. This could be because:
You want to do something else before or after
std::make_unique / std::make_shared need to wrap it in the unique/shared_ptr variable
A benchmarking library might wrap a function call with timers
You want to do something slightly different std::vector::emplace uses uninitialised memory construction
You want to add an extra parameter (eg. always call a function with the last parameter as 1)
This isn’t usually very useful, because it can be achieved with std::bind or lambda functions.
5.8

Feedback
6