advanced c++ - brown university · /43 1.overview-c++ expressions fall into one of several...

46
/43 Advanced C++ 1

Upload: phamtu

Post on 21-Apr-2018

215 views

Category:

Documents


2 download

TRANSCRIPT

/43

Advanced C++

1

/43

Overview

1. Expression Value Categories2. Rvalue References3. Templates4. Miscellaneous Hilarity

2

/43

1. Expression Value Categories“These are not the droids you’re looking for”

~Obi-wan Kenobi

3

/43

1. Expression Value Categories

1. Overview2. lvalues3. prvalues4. xvalues5. glvalues6. rvalues7. Recap

4

/43

1. Overview

- C++ expressions fall into one of several categories

Expression

glvalue rvalue

xvaluelvalue prvalue

5

/43

1. Overview

- There are three fundamental value types- lvalue- xvalue- prvalue

- glvalues are either lvalues or xvalues- rvalues are either xvalues or prvalues- As you can see, xvalues can be either

glvalues or rvalues6

/43

2. lvalues

- An lvalue is an expression that designates a function or an object

- Historically, lvalues could appear on the left side of an equals sign

- Example:- int x = 5; // x is an lvalue- int y = x; // y is an lvalue- x = 700; // x is an lvalue

7

/43

2. lvalues (cont.)

- lvalues are named expressions- hence they can appear on the left side of =

- Named objects are always lvalues- int x, int &x, and int &&x are all lvalues

- Arrays are always lvalues- Functions returning references are lvalues

- int& func() {...}- func() = 11;

8

/43

3. prvalues

- All literals except string literals are prvalues- String literals are of type “array of n const char*” where

n is the length of the string- As we know, arrays are lvalues

- Intermediate expressions are prvalues- int x = 5; // 5 is a prvalue- int y = x+2; // x+2 evaluates to a prvalue

9

/43

4. xvalues

- xvalues, or “eXpiring values,” are expressions that are usually near the end of their lifetime

- For example, rvalue references returned by functions

10

/43

5. glvalues

- glvalues are either lvalues or xvalues- They are “General lvalues”- They’re everything that is not a prvalue

11

/43

6. rvalues

- rvalues are either xvalues or prvalues- rvalues are usually temporary objects or

values not associated with objects- int x = 5;- int y = x + 3; // x + 3 is a temporary

// value, thus an rvalue

- (x + 3) = 7; // Error!// x + 3 is an rvalue

12

/43

7. Recap

- Expression value categories are nitpicky- Many complex situations render certain

expressions one or the other- An expression is an xvalue if, for example, it is a class member access expression designating a

non-static data member of non-reference type in which the object expression is an xvalue

- Prior to C++11, there were only lvalues and rvalues- But even then there were still lots of conditions and

subtypes and exceptions to rules13

/43

7. Recap

- Roughly speaking- lvalue = named thing, can be assigned to- prvalue = temporary object or literal, can’t be

assigned to- xvalue = certain instances of rvalue references

- That’s a rough gist of things- It would take a whole presentation to go

through the system in fine detail- Study expression value categories more! 14

/43

2. Rvalue References

15

/43

2. Rvalue References

1. Motivation2. Moving3. Perfect Forwarding

16

/43

1. Motivation

- Before C++11, C++ suffered from two significant problems

- Moving: What if I don’t want to copy, but instead to move a resource from here to there?

- Forwarding: How can I make two function calls equivalent?

17

/43

1. Motivation

- Rvalue references solve these problems- Rvalue references are references to

temporary expressions (rvalues)- denoted with a double ampersand &&

- In other words, they are references to expressions that will very soon disappear

- The following use cases should motivate this seemingly abstract definition

18

/43

2. Moving

- Prior to C++11, you could only copy an object- You’d end up with two instances of that class’s

resource- Frequently, this wouldn’t make sense, so copying was

disabled as good practice- Consider the following example:

19

/43

// Texture.h

class Texture

{

public:

Texture(int width, int height) : m_id(0), m_width(width), m_height(height) // Constructor

{

glGenTextures(1, &m_id);

}

Texture(const Texture &t); // Copy constructor

Texture& operator=(const Texture &t); // Copy assignment operator

~Texture(); // Destructor

{

glDeleteTextures(1, &m_id);

}

int getWidth() const;

int getHeight() const;

private:

int m_id;

int m_width;

int m_height;

};20

/43

2. Moving

- What should that copy functionality do?- Should we duplicate that ID?

- What happens when the first object’s destructor gets called?

- That texture ID gets snuffed- Now the second object has an invalid ID…

- Should we enforce some special copying idiom?- No, we’d break far too many good practices 21

/43

2. Moving

- Moving is a new feature in C++11- Rather than forcing you to end up with two

objects through copying- Or forcing you to use only one object without

changing its memory location- You may now move an object from one

memory location to another22

/43

2. Moving

- A move is done with the knowledge that the object you are moving will not be accessible after the move- That sounds like an rvalue reference

- Contrast to a copy, where the original copy is still valid.

23

/43

// Texture.h

class Texture

{

public:

Texture(int width, int height) : m_id(0), m_width(width), m_height(height) // Constructor

{

glGenTextures(1, &m_id);

}

Texture(const Texture &t); // Copy constructor

Texture& operator=(const Texture &t); // Copy assignment operator

Texture(const Texture &&t); // Move constructor (m-ctor)

Texutre& operator=(const Texture &&t); // Move assignment operator (m-ao)

~Texture(); // Destructor

{

glDeleteTextures(1, &m_id);

}

...

};

&& denotes rvalue reference

24

/43

2. Moving

- In the m-ao and m-ctor, we know that the passed in variable is no longer in use

- We can copy that texture id to the new texture and zero it in the argument

- Now when the original texture’s destructor gets called, it will safely delete a 0-texture- The new texture’s id will be left intact.

25

/43

2. Moving

- std::move converts objects into rvalue references

- Use it to manually move objects when rvalue references are not already present

- Especially useful when used with Standard Library classes like std::vector and std::map

26

/43

2. Moving

- Still unclear?- See this excellent answer on StackOverflow

27

/43

3. Templates

28

/43

3. Templates

1. Syntax2. Type Deduction3. Template Metaprogramming

29

/43

Syntax

- Function templates- Functions parametrized by type- Can use “typename” or “class” in angle brackets

- They mean the same thing

template <typename Type>

Type max(Type a, Type b) {

return a > b ? a : b;

}

30

/43

Syntax continued

- Class templates- Similar to function templates- Examples: std::vector, std::map, ...

31

/43

Syntax continued

- Variable templates- Like a variable that can take on different types

template<class T>

constexpr T pi = T(3.1415926535897932385); // variable template

template<class T>

T circular_area(T r) // function template

{

return pi<T> * r * r; // pi<T> is a variable template instantiation

}

32

/43

Type Deduction

- Say you’ve defined the “max” function template

// This will call max<int> by implicit argument deduction.

std::cout << max(3, 7) << std::endl;

// This will call max<double> by implicit argument deduction.

std::cout << max(3.0, 7.0) << std::endl;

33

/43

Type Deduction continued

- Say you’ve defined the “max” function template

std::cout << max(3, 7.0) << std::endl; // Works sometimes, depending on compiler

std::cout << max<double>(3, 7.0) << std::endl; // Sometimes have to specify max<double> explicitly

34

/43

Template Metaprogramming● C++ allows us to parametrize types on constant values● Makes it possible to write methods that are executed at

compile time● Example on next slide

35

/43

Template Metaprogramming example// Define struct template, parameterized on constant int value

template <int N>

struct Factorial

{

// Enum value created at compile time, evaluates to N * (N - 1)! = N!

enum { value = N * Factorial<N - 1>::value };

};

// Special case struct template when parametrized on 0 value

template <>

struct Factorial<0>

{

enum { value = 1 };

};

void foo()

{

int x = Factorial<4>::value; // == 24

int y = Factorial<0>::value; // == 1

} 36

/43

4. Miscellaneous HilarityIn no particular order...

37

/43

4.a. Nameless namespaces

- In C++ you can have anonymous namespaces

- They were supposed to replace keyword static, although that’s basically never happening

38

/43

4.b. #define private public

- This does exactly what you think it does- If you made it this far, you must be able to

see why this is...not recommended

39

/43

4.c. func(void);

- Declaring a function with a void parameter will work, as C++ was derived from C

- However, in C++, void is already the default- You just look like a C programmer

40

/43

4.d. Covariant Return Types

- Virtual functions in derived classes may explicitly return classes derived from the base class function’s original return value

- Code example is much clearer:

41

/43

class BaseB

{

...

}

class DerivedB : public BaseB

{

...

}

class BaseA

{

virtual BaseB func();

}

class DerivedA

{

DerivedB func() override; // DerivedB isa BaseB. This is logically and linguistically legal

}

42

/43

4.e. Friends

- A class may declare another class a friend- Friend classes may access private and

protected variables and functions- Friendship is not automatically reciprocal

- Just because Foo is a friend of Bar- Does not mean that Bar is a friend of Foo- This is reasonable: You don’t want someone to be

able to friend themselves into a class whenever they want to. 43

/34

2. Keyword decltype

- Keyword decltype is used for deducing the declared type of an identifier- declared type = decltype. Get it? It’s clever, right?

- decltype is unique in that it “returns” an actual type, not a variable- “returns” because it looks like a function call

producing a type- Best seen through example:

44

/34

// main.cppint main(int argc, char *argv[]) {

int x = 5;

decltype(x) y = 7; // y’s type is int

}

45

/34

2. Keyword decltype

- Keyword decltype is useful for template programming- We’ll get into this later

- Otherwise it’s not something you’ll usually run into

46