advanced c++ - brown university · /43 1.overview-c++ expressions fall into one of several...
TRANSCRIPT
/43
Overview
1. Expression Value Categories2. Rvalue References3. Templates4. Miscellaneous Hilarity
2
/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
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
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.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