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

COMP6771 Advanced C++ Programming
Week 8.1 Advanced Templates
1

demo801-default.h
demo801-default.cpp
Default Members
1 #include 2
3 template>
4 class stack {
5 public:
6 stack();
7 ~stack();
8 auto push(T&) -> void;
9 auto pop() -> void;
10 auto top() -> T&;
11 auto top() const -> T const&;
12 static int num_stacks_;
13
14 private:
15 CONT stack_;
16 };
17
18 template
19 int stack::num_stacks_ = 0;
20
21 template
22 stack::stack() {
23 num_stacks_++;
24 }
25
26 template
27 stack::~stack() {
28 num_stacks_–;
29 }
We can provide default arguments to template types (where the defaults themselves are types)
It means we have to update all of our template parameter lists
1 2 3 4 5 6 7 8 9
10
#include
#include “./demo801-default.h”
auto main() -> int {
auto fs = stack{};
}
stack is1, is2, is3;
std::cout << stack::num_stacks_ << "\n"; std::cout << stack::num_stacks_ << "\n"; 2 Specialisation The templates we've defined so far are completely generic There are two ways we can redefine our generic types for something more specific: Partial specialisation: Describing the template for another form of the template T* std::vector
Explicit specialisation:
Describing the template for a specific, non-generic type std::string
int
3.1

When to specialise
You need to preserve existing semantics for something that would not otherwise work
std::is_pointer is partially specialised over pointers You want to write a type trait
std::is_integral is fully specialised for int, long, etc.
There is an optimisation you can make for a specific type
std::vector is fully specialised to reduce memory footprint
3.2

When not to specialise Don’t specialise functions
A function cannot be partially specialised
Fully specialised functions are better done with overloads
Herb Sutter has an article on this
http://www.gotw.ca/publications/mill17.htm
You think it would be cool if you changed some feature of the class for a specific type
People assume a class works the same for all types
Don’t violate assumptions!
3.3

Our Template
Here is our stack template class
stack.h stack_main.cpp
1 #include
2 #include
3 #include
4
5 template
6 class stack {
7 public:
8 auto push(T t) -> void { stack_.push_back(t); }
9 auto top() -> T& { return stack_.back(); }
10 auto pop() -> void { stack_.pop_back(); }
11 auto size() const -> int { return stack_.size(); };
12 auto sum() -> int {
13 return std::accumulate(stack_.begin(), stack_.end(), 0);
14 }
15 private:
16 std::vector stack_;
17 };
1 auto main() -> int {
2 int i1 = 6771;
3 int i2 = 1917;
4
5 stack s1;
6 s1.push(i1);
7 s1.push(i2);
8 std::cout << 9 std::cout << 10 std::cout << 11 } s1.size() << " "; s1.top() << " "; s1.sum() << "\n"; 3.4 Partial Specialisation In this case we will specialise for pointer types. Why do we need to do this? You can partially specialise classes You cannot partially specialise a particular function of a class in isolation The following a fairly standard example, for illustration purposes only. Specialisation is designed to refine a generic implementation for a specific type, not to change the semantic. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 template
class stack {
public:
auto push(T* t) -> void { stack_.push_back(t); }
auto top() -> T* { return stack_.back(); }
auto pop() -> void { stack_.pop_back(); }
auto size() const -> int { return stack_.size(); };
auto sum() -> int{
} private:
};
return std::accumulate(stack_.begin(),
stack_.end(), 0, [] (int a, T *b) { return a + *b; });
std::vector stack_;
1 #include “./demo802-partial.h” 2
3 auto main() -> int {
4 auto i1 = 6771;
5 auto i2 = 1917;
6
7 auto s1 = stack{};
8 s1.push(i1);
9 s1.push(i2);
10 std::cout << 11 std::cout << 12 std::cout << 13 } s1.size() << " "; s1.top() << " "; s1.sum() << "\n"; demo802-partial.h demo802-partial.cpp 3.5 Explicit Specialisation Explicit specialisation should only be done on classes. std::vector is an interesting example and here too
std::vector::reference is not a bool&
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
#include
template
struct is_void {
static bool const val = false;
};
template<>
struct is_void {
static bool const val = true;
};
auto main() -> int {
std::cout << is_void::val << "\n"; std::cout << is_void::val << "\n"; } demo803-explicit.cpp 3.6 Type Traits Trait: Class (or class template) that characterises a type 1 2 3 4 5 6 7} #include
#include auto main() -> int {
std::cout << std::numeric_limits::min() << "\n"; std::cout << std::numeric_limits::min() << "\n"; 1 2 3 4 5 6 7 8 9} static auto min() -> T;
static auto min() -> int { return -INT_MAX – 1; }
template
struct numeric_limits {
};
template <>
struct numeric_limits {
10
11 template <>
12 struct numeric_limits {
13 static auto min() -> float { return -FLT_MAX – 1; }
14 }
This is what might look like
4.1

Type Traits
Traits allow generic template functions to be parameterised
1 2 3 4 5 6 7 8 9
10
11
12
13
14
15
16
17
18
19
20
#include
#include
#include template
T findMax(const std::array& arr) {
}
T largest = std::numeric_limits::min();
for (auto const& i : arr) {
if (i > largest)
largest = i;
}
return largest;
auto main() -> int {
auto i = std::array{-1, -2, -3};
std::cout << findMax(i) << "\n"; auto j = std::array{1.0, 1.1, 1.2};
std::cout << findMax(j) << "\n"; } demo804-typetraits1.cpp 4.2 Two more examples Below are STL type trait examples for a specialisation and partial specialisation This is a good example of partial specialisation http://en.cppreference.com/w/cpp/header/type_traits 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include
template
struct is_void {
};
static const
bool val = false;
{
bool val = true;
is_void::val << "\n"; is_void::val << "\n"; template<>
struct is_void
static const
};
auto main() -> int {
std::cout << std::cout << } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include
template
struct is_pointer {
static const bool val = false;
};
template
struct is_pointer {
static const bool val = true;
};
auto main() -> int {
std::cout << is_pointer::val << "\n"; std::cout << is_pointer::val << "\n"; } demo805-typetraits2.cpp demo806-typetraits3.cpp 4.3 Where it's useful Below are STL type trait examples http://en.cppreference.com/w/cpp/header/type_traits 1 2 3 4 5 6 7 8 9} #include
#include
template
auto testIfNumberType(T i) -> void {
10 else { 11
12
13 }
if (std::is_integral::value || std::is_floating_point::value) {
std::cout << i << " is a number" << "\n"; std::cout << i << " is not a number" << "\n"; 14 } 15 16 auto main() ->
17 auto i
18 auto l
19 auto d
20 testIfNumberType(i);
21 testIfNumberType(l);
22 testIfNumberType(d);
23 testIfNumberType(123);
24 testIfNumberType(“Hello”);
25 auto s = “World”;
26 testIfNumberType(s);
27 }
int {
= int{6};
= long{7};
= double{3.14};
demo807-typetraits4.cpp
4.4

Variadic Templates
These are the instantiations that will have been generated
1 #include
2 #include
3
4 template
5 auto print(const T& msg) -> void {
6 7} 8
9
std::cout << msg << " "; 10 11 12 13 14 15 16 17 18 19 20 template
auto print(A head, B… tail) -> void {
print(head);
print(tail…);
}
auto main() -> int {
print(1, 2.0f);
}
std::cout << "\n"; print(1, 2.0f, "Hello"); std::cout << "\n"; 9 10 11 12 13 14 15 16 17 18 19 20 21 auto print(float b, const char* c) -> void {
print(b);
print(c);
auto print(int const& a) -> void {
std::cout << a << " "; } auto print(int a, float b, const char* c) -> void {
print(a);
1 auto print(const char* const& c) -> void { 2 std::cout << c << " "; 3} 4 5 auto print(float const& b) -> void { 6 std::cout << b << " "; 7} 8 } } print(b, c); demo808-variadic.cpp 5 Member Templates Sometimes templates can be too rigid for our liking: Clearly, this could work, but doesn't by default 1 #include 2
3 template
4 class stack {
5 public:
6 auto push(T& t) -> void { stack._push_back(t); }
7 auto top() -> T& { return stack_.back(); }
8 private:
9 std::vector stack_;
10 };
11
12 auto main() -> int {
13 auto is1 = stack{};
14 is1.push(2);
15 is1.push(3);
16 auto is2 = stack{is1}; // this works
17 auto ds1 =
18 stack{is1}; // this does not
19 }
6.1

Member Templates
Through use of member templates, we can extend capabilities
1 #include 2
3 template
4 class stack {
5 public:
6 explicit stack() {}
7 template
8 stack(stack&);
9 auto push(T t) -> void { stack_.push_back(t); }
10 auto pop() -> T;
11 auto empty() const -> bool { return stack_.empty(); }
12 private:
13 std::vector stack_;
14 };
15
16 template
17 T stack::pop() {
18 T t = stack_.back();
19 stack_.pop_back();
20 return t;
21 }
22
23 template
24 template
25 stack::stack(stack& s) {
26 while (!s.empty()) {
27 stack_.push_back(static_cast(s.pop()));
28 }
29 }
1 auto main() -> int {
2
3
4
5
6
7
8 9}
auto is1 = stack{};
is1.push(2);
is1.push(3);
auto is2 = stack{is1}; // this works
auto ds1 =
stack{is1}; // this does not work
// until we do the changes on the left
demo809-membertemp.cpp
6.2

Template Template Parameters
Previously, when we want to have a Stack with templated container type we had to do the following:
1 template typename CONT> 2 class stack {}
What is the issue with this?
Ideally we can just do:
1 #include
2 #include
3
4 auto main(void) -> int {
5 stack> s1;
6 s1.push(1);
7 s1.push(2);
8 std::cout << "s1: " << s1 << "\n"; 9 10 stack> s2;
11 s2.push(1.1);
12 s2.push(2.2);
13 std::cout << "s2: " << s2 << "\n"; 14 //stack> s2; :O
15 }
1 #include
2 #include
3
4 auto main(void) -> int {
5 stack s1;
6 s1.push(1);
7 s1.push(2);
8 std::cout << "s1: " << s1 << std::endl; 9 10 stack s2;
11 s2.push(1.1);
12 s2.push(2.2);
13 std::cout << "s2: " << s2 << std::endl; 14 } 7.1 Template Template Parameters 1 #include
2 #include
3
4 template
5 class stack {
6 public:
7 auto push(T t) -> void { stack_.push_back(t); }
8 auto pop() -> void { stack_.pop_back(); }
9 auto top() -> T& { return stack_.back(); }
10 auto empty() const -> bool { return stack_.empty(); }
11 private:
12 CONT stack_;
13 };
1 auto main(void) -> int {
2 stack> s1;
3 int i1 = 1;
4 int i2 = 2;
5 s1.push(i1);
6 s1.push(i2);
7 while (!s1.empty()) {
8 std::cout << s1.top() << " "; 9 s1.pop(); 10 } 11 std::cout << "\n"; 12 } 1 #include
2 #include
3 #include
4
5 template typename CONT>
6 class stack {
7 public:
8 auto push(T t) -> void { stack_.push_back(t); }
9 auto pop() -> void { stack_.pop_back(); }
10 auto top() -> T& { return stack_.back(); }
11 auto empty() const -> bool { return stack_.empty(); }
12 private:
13 CONT stack_;
14 };
1
2
3
4
5
6
7 8}
#include
#include
auto main(void) -> int {
auto s1 = stack{};
s1.push(1);
s1.push(2);
demo810-temptemp.cpp
7.2

Template Argument Deduction
Template Argument Deduction is the process of determining the types (of type parameters) and the values of nontype parameters from the types of function arguments.
type paremeter non-type parameter
1
2
3
4
5
6}
7 return min; 8}
template
T findmin(const T (&a)[size]) {
T min = a[0];
for (int i = 1; i < size; i++) { if (a[i] < min) min = a[i]; call parameters 8.1 Implicit Deduction Non-type parameters: Implicit conversions behave just like normal type conversions Type parameters: Three possible implicit conversions ... others as well, that we won't go into 1 // array to pointer 2 template
3 f(T* array) {}
4
5 int a[] = { 1, 2 };
6 f(a);
1 // conversion to base class
2 // from derived class
3 template
4 void f(base &a) {}
5
6 template
7 class derived : public base { }
8 derived d;
9 f(d);
1 // const qualification
2 template
3 f(const T item) {}
4
5 int a = 5;
6 f(a); // int => const int;
8.2

Explicit Deduction
If we need more control over the normal deduction process, we can explicitly specify the types being passed in
1 template 2 T min(T a, T b) {
3 4} 5
6 auto main() -> int {
7 auto i = int{0};
8 auto d = double{0};
9 min(i, static_cast(d)); // int min(int, int)
10 // min(i, d); // int min(int, int)
11 min(static_cast(i), d); // double min(double, double)
12 min(i, d); // double min(double, double)
13 }
return a < b ? a : b; demo811-explicitdeduc.cpp 8.3 Feedback 9