Presentation is loading. Please wait.

Presentation is loading. Please wait.

Advanced OOP CS 440/540 Spring 2019 Kenneth Chiu

Similar presentations


Presentation on theme: "Advanced OOP CS 440/540 Spring 2019 Kenneth Chiu"— Presentation transcript:

1 Advanced OOP CS 440/540 Spring 2019 Kenneth Chiu
Accelerated C/C++ Advanced OOP CS 440/540 Spring 2019 Kenneth Chiu

2 Purpose I’m assuming all of you have had a first-pass in C++.
Basic style Basic programming approach Learn iteratively. Content-based on what has tripped me up in the past. Why is C++ the way it is? Sometimes a language may have a mistake. What is a mistake in a language? Learn iteratively: Second-pass to hit things that you didn’t have the background/context to think/appreciate about the first time through. Language decisions are made by people just like you. And sometimes, they make mistakes.

3 Compilation Process What happens when we compile?
What is object code? Invoked via a front-end driver: Run the preprocessor. Run the compiler. Run the assembler. Run the linker. [Show] What happens when you run a compiled program? OS loads the file into memory. Sets up various memory regions. May do some runtime linking/relocation. Jump to main. Compilation is the process of transforming a language from one to another. Object code is the usual output of compilation. It is machine instructions along with linking and other info. Each thread requires its own stack. Some segments may not be writable, because they may be shared. Stack may not be executable, to help prevent buffer overflow attacks.

4 Language Runtime Close correspondence between machine and source.
i = 2 + j; Not a close correspondence. if (typeid(A) == typeid(B)) { … } Such machine code is said to be part of the language runtime. Source code that causes no machine code to be generated?

5 Types of Violations of the Standard
What is a language standard, what does it specify? Does it tell you what you can or cannot do? Not really. It specifies constraints on the language. It says, if you do A, then B happens. It’s a contract, essentially. What is a “violation” of the standard? It’s imprecise word, but commonly used to mean something not in accordance with some aspect of the standard. What happens when your program violates the standard? Does it mean the program won’t compile? Will it crash? Can a compiler also violate the standard? Are there different ways that a program can “violate” the standard? If your program violates the standard, many things could happen. To further clarify, we need to clarify what we mean by “violate”. A compiler could “violate” the standard, which means that it doesn’t follow some rule properly. For example, if the rule is that: char *p = ...; assert(p == (char *) (void *) p); must pass, and some compiler doesn’t support it in some cases, then the compiler would be violating the standard.

6 What will this print out?
#include <iostream> int main() { char i = 0; i--; std::cout << (int) i << std::endl; } Prints out -1 when compiled on remote.cs.binghamton.edu. Is it guaranteed to print this out? In every occurrence? How can we find out what it will print out? The compiler vendor needs to tell you if char is signed or unsigned? Known as implementation-defined behavior. void foo(int i1, int i2) { } foo(printf(“First.\n”), printf(“Second.\n”)); You can’t. The compiler vendor can change it at will. Known as unspecified behavior. What will this do? int *a = 0; *a = 1; Anything could happen. Anything. Known as undefined behavior. Note that overflow of a signed integer invokes undefined behavior.

7 Implications of “undefined behavior” for the Implementer
As the implementer, what do you do if the specification says that the result is undefined behavior? For example, let’s say the function below is specified as: void foo(int *); If the pointer is null, the behavior is undefined. If the pointer does not point to the first element of an array of two integers, then the behavior is undefined. Implementer is allowed to pretend it never happens. So do all languages have UB? Why allow it? [Show undefined_behavior.] Do you need to check whether or not the pointer is null? Do you need to do anything to try to check whether or not it’s actually an array of two ints? Not all languages have undefined behavior. The primary reason for it is efficiency.

8 Compile-Time vs. Run-Time
What kind of error is better? All else being equal, there’s no advantage to making it run-time. [Show run_time_vs_compile_time.] However, to turn that run-time error into a compile-time error usually means using a strict, statically-typed language. Those languages are usually thought of as being not as flexible. For a given, actual error, there is no advantage to making it run-time. However, certain language features will turn compile-time errors into run-time errors, but provide some more flexibility. One example is dynamic typing. Whether or not the trade-off is worth it depends.

9 Preprocessor #include “A.hpp” #define macro(a, b) #if #ifdef, #ifndef
#if FOO == 3 #if defined(a) #endif #else, #elif #ifdef, #ifndef #error, #pragma __LINE__ (integer literal), __FILE__ (string literal), __DATE__ (string literal), __TIME__ (string literal) __cplusplus # (stringification) and ## (token gluing) in expansions [Show] With angle brackets, include will look in system-places, not in current. __LINE__ gives line number. __FILE__ gives file name. __DATE__ and __TIME__ do the obvious. __cplusplus is defined if compiled by C++ compiler. Show using -E_option.

10 -E (option to show results after preprocessing)
[Show preprocessor_output_option.] Line continuation is a backslash at the end. (Make sure you don’t have a space after the backslash.) What’s wrong with this? #define MY_MACRO(t) \ void foo_##t() { \ // This is a comment \ call_something(); \ } The code snippet has a problem because the // comments out to the end of the line. But it’s actually just one long line due to the line continuation character. So the rest of the macro after the comment is actually commented out.

11 Multi-line macros: #define macro(x) \ cout << x << endl; \ cout << “DEBUGGING” << endl; Good? if (condition) macro(my_var); #define macro(x) \ do { \ cout << x << endl; \ /* Etc. */ \ } while (0) Why did we leave off the semicolon at the end? If the semicolon is added to the end of the do {…} while (0), then it will fail when used in if-else: if (…) do { …. } while (0);; else foo;

12 Macros can call other macros, but cannot be used recursively.
#define apply(m, x) m(x) #define m1(x) x + x #define m2(x) x*x apply(m1, i)  i + i apply(m2, i)  i*i

13 Conditional compilation:
Suppose you have code that should compile on both Linux and Win32, but you need to call a different function in each. What do you do? Why not just have two versions of your source code? Conditional compilation: #ifdef __linux some_linux_func(…); #else some_win32_func(…); #endif Is this good? #ifdef __linux some_linux_func(…); #elif defined (__win32_preprocessor_symbol) some_win32_func(…); #else #error Unknown platform #endif To show all predefined macro symbols, use –E –dM. Keeping two completely independent versions seems like a waste of effort if both versions have almost the same functionality. If you try to maintain it, you’ll have to make bug fixes in two different files. What happens to the first example if compiled on Solaris?

14 Predefined identifiers: These are not macros
__func__: A static const character array initialized with the current function name. void some_func() { cout << __func__ << endl; } void some_other_func() { cout << __func__ << endl; } Why aren’t these macros? Not a macro because preprocessor can’t figure it out.

15 Variadic macros #define foo(…) func(_VA_ARGS_)
Let’s say we wanted to print the file name and line number with all of our debug statements. [Show debug_print.] #include <stdio.h> #define dbg(fmt, ...) \ fprintf(stderr, "%s[%d]: " fmt "\n", __FILE__, __LINE__, ## __VA_ARGS__) int main() { dbg("%d, %d", 1, 2); dbg("%d, %d, %f", 1, 2, 3.14); dbg("Uh-oh!"); }

16 Assertions Assertions are ways of “asserting” that certain things are true. By putting in an assertion, you are saying that if the expression being asserted is not true, then something is very seriously wrong. What kind of assertions might you use here? delet(Animal *a) { … } Animal *pop_front(List *l) { … } To make assertions work, include this header file: #include <assert.h> Possible assertions: assert(a != 0); assert(list->size > 0);

17 Assert liberally. Preconditions Postconditions Loop invariants
Any condition that before a section of code that that code relies on for correctness. Postconditions Any condition after a section of code that that code will preserve, if it was correct. For example, in a BST, you know that the left child must be greater than or equal to the right child. Loop invariants Any condition within a loop that your code relies on for correctness. Consider code for binary search. Let’s say that it maintains an index to the beginning of the current search region (begin_index), and a pointer to the end of the current search region (end_index). What assertion can you put in at the end of the loop? while (...) { assert(???); } Can assert that begin_index is <= end_index.

18 Use to check return codes.
A quick and dirty way of checking error codes. Gives you 80% of the benefit, with 5% of the effort. if ((rv = some_lib_or_sys_call(…)) < 0) { fprintf(stderr, “…”); abort(); } rv = some_lib_or_sys_call(…); assert(rv == 0); To compile-out the assertions, define NDEBUG. g++ -DNDEBUG foo.cpp I strongly urge that you check the return value from every single library or system call. (Except printf().)

19 How is the assert() macro defined?
How do we find it? #ifndef NDEBUG #define assert(e) \ ((e) ? static_cast<void>(0) \ : fail(#e, __FILE__, __LINE__, __func__)) #elif #define assert(e) #endif What if we have an assertion that is low-cost, so we always want it to be included, even in production code? #define check(e) \ ((e) ? static_cast<void>(0) \ : fail(#e, __FILE__, __LINE__, __func__))

20 Assertions vs. Exceptions vs. Special Return Values
What are the possible behaviors you could implement for these conditions? Animal *find(const char *type_name); Normally returns the found Animal. What should you do if the animal is not found? int read(int fd, char *buf, int count); Normally returns count of bytes read. What should you do if it is the end of file? double sqrt(double x); Normally returns the square root of the number. What should you do if the number is negative? int send_socket(int sock_fd, const char *buf, int count); Normally returns the count of the number of bytes sent. What if the network is down? void *lookup_in_hash_table(const char *key); Normally returns the value that is found (which is a void * for this particular hash table). What if the key is not found? void *malloc(size_t sz); What if there is no more memory?

21 Compile-Time Assertions
assert() is strictly run-time. You won’t know till you run the program. How can you assert things at compile-time? A limited number of things can be checked in the preprocessor: #define FOO 2 #if FOO < 4 … #endif #if sizeof(long) < 8 #error Type long is too small. #endif C++11 supports static_assert, which can check basically anything that is a compile-time constant (constexpr). static_assert(sizeof(long) >= 8, “Type long is too small.”); (Message is optional in C++17.) C++98 can use boost static asserts. Can’t actually do sizeof in preprocessor.

22 Comments How are these comments?
// Define an int variable i in the for-loop, and // initialize it to 0. Execute the for-loop as long // as i is less than the value of a.size(). At the // end of each iteration, increment i by 1. In the // body of the for-loop, multiple a[i] by 2, and // assign it to a[i]. for (int i = 0; i < a.size(); i++) { a[i] *= 2; } // Add 7 to x, then bitwise AND it with the bitwise // complement of 0x7. x = (x + 7) & ~0x7; // Call compute_y() with i and x as parameters. compute_y(i, x); // Initialize health to 1. double health = 1.0; These comments are really just putting the code in words. They don’t add anything that is not already in the code.

23 Better? // Double each element in array a. for (int i = 0; i < a.size(); i++) { a[i] *= 2; } // Round up x to next multiple of 8. x = (x + 7) & ~0x7; // Compute y-coordinate of ith // player given a fixed // x-coordinate. compute_y(i, x); // Create monsters starting at // health 1.0, since health 0 means // dead. double health = 1.0; These comments help give context and background that might not be obvious from the code.

24 Do comment the non-obvious.
Don’t say the obvious. // Initialize x. x = 1; Do comment the non-obvious. // Round up x to next multiple of 8 x = (x + 7) & ~0x7; If working in a team, consider leaving your initials/name in comments that might need explanation. // x cannot be defined as a long due to a bug // in the Solaris compiler. –ken

25 Let’s say you have 1000 lines of code that you want to comment out for some reason:
for (int i = 0; i < j; i++) { // 1000 more lines. // ... How do you do it? /* /* This is a comment. */ */ Use #if 0 to comment out large sections. It will nest. // Works. #if 0 #if 0 // ... #endif #endif

26 Source code Organization

27 C++/C Source Code Organization
Why break code up into multiple files? Ease of finding things? Compilation speed. Only need to recompile part of the app. Known as separate compilation Libraries Reuse? If I put a class separately into A.cpp, it is easier to move to another application.

28 Okay, why split into header file and implementation file
Okay, why split into header file and implementation file? What (bad) things would happen if we did not? For libraries, the case is clear. Need declarations to tell the compiler how to call code. What about your application? Why not put everything into A.cpp, like in Java? Suppose B.cpp needs to use class A. The compiler needs the declarations. Why not just include the whole source file? Why need header file if already linking the library? Because the link happens after the assembly code for a call is generated. Even if the compiler knew the libraries early, it can’t find the calling information. Why need library if already have the header file? The header file tells the compiler how to call something, but there still has to be some code there to call. Are you satisfied with these answers? What’s the meta-question here? (Why isn’t this an issue in Java?) Can’t include the whole source file because then we’ll have multiple definitions of the functions. Also, then it would be huge. For example, you would need to include practically the whole C library into every file. In Java, the system is more sophisticated. Declarations can be put into .java files. It can know how to get just the declarations.

29 The header-file/implementation-file split is a convention.
The standard does not dictate what goes in a header file. However, the design of C++ does strongly influence best practices. What goes in a header file? The minimum amount necessary for the implementation (classes and/or declarations of standalone functions) to be used by other files. In other words, you divide the code into two chunks. In the first chunk, you put everything that is needed to use your code. Such as call it, or if a class, define an instance of the object, etc. This is the header file (.hpp). Everything else goes in the second chunk. This is the implementation file (.cpp) file. Basically, no definitions except class definitions go in header files.

30 A.hpp B.hpp C.hpp A.cpp B.cpp C.cpp main.cpp A.o B.o C.o main.o
includes A.cpp B.cpp C.cpp main.cpp compiled to A.o B.o C.o main.o Dotted blue line means includes. link a.out (exe) Libraries

31 What do these code snippets need?
How many times per executable is a header file compiled? What about implementation file? If something can go in either, should we put it in the header file or implementation file? What do these code snippets need? obj->a_member void foo(MyClass *) {} obj->foo(); Where do these go? Class definitions Function definitions Function declarations Global variable declarations Global variable definitions Basically, no definitions except class definitions go in header files.

32 Translation Unit Consider this code fragment from a file named foo.cpp: … void foo() { goo(); } … Are either of these statements is clear and unambiguous? “The call to goo() will be a syntax error if there is no declaration of it in this file.” “The call to goo() will be a syntax error if this file doesn’t declare goo(), and this file doesn’t (recursively) include any header files that declare goo().” This suggests that we should have a new term: A translation unit is the result of reading in a file, after all processing of included files and conditional compilation. “This will be a syntax error if there is no declaration of goo() in the translation unit.”

33 Handling Global Variables
How do you use global variables when you split things into files? Does this work? File1.cpp int a; void f() { // Access a. // … } File2.cpp int a; void g() { // Access a. // … } Doesn’t work because you get multiply defined symbols. $ g++ File1.cpp File2.cpp ...

34 One Definition Rule (ODR)
In C and C++, each variable can be defined only once. You can declare a global variable by using extern. Defining (no extern) actually creates a variable. Declaring (by using extern) states the existence of a variable, and indicates that it was defined somewhere else, so tells the linker to go look for it. So, a global variable should be defined in one translation unit and declared in all others that use it. How to fix previous? To fix previous, need to declare in one of the files with extern, and define in the other file without extern.

35 Of course, you should probably be more systematic about it:
You need to have: File2.cpp extern int a; void g() { // Access a. // … } File1.cpp int a; void f() { // Access a. // … } Of course, you should probably be more systematic about it: globals.cpp int a; double x; globals.hpp extern int a; extern double x; File1.cpp #include “globals.hpp” void f() { // Access a. // … } File2.cpp #include “globals.hpp” void g() { // Access a. // … } $ g++ globals.cpp File1.cpp File2.cpp ...

36 There is a lot of redundancy between globals. hpp and globals. cpp
There is a lot of redundancy between globals.hpp and globals.cpp. Imagine if it were a very large file. Anyway to avoid it? globals.cpp int a; double x; // ... // Zillions of them globals.hpp extern int a; extern double x; // ... // Zillions of them

37 Leverage the preprocessor:
globals.hpp #ifndef XYZZY_GLOBALS_HPP #define XYZZY_GLOBALS_HPP #include <A.hpp> #ifndef XYZZY_GLOBALS_EXTERN #define XYZZY_GLOBALS_EXTERN extern #endif XYZZY_GLOBALS_EXTERN A a; XYZZY_GLOBALS_EXTERN double x; #endif File2.cpp #include “globals.hpp” void g() { // Access a. // … } globals.cpp #define XYZZY_GLOBALS_EXTERN #include “globals.hpp” $ gcc File1.cpp File2.cpp globals.cpp Isn’t this actually more complicated?

38 ODR, Revisited Let’s say you are the linker implementer. Could you make this work if you wanted to? [Show multiple_definitions_2] File1.cpp int a; void f() { // Access a. // … } File2.cpp int a; void g() { // Access a. // … } $ g++ File1.cpp File2.cpp ...

39 We could make this work, but which one?
What about this? We could make this work, but which one? At some point, rules become too complicated. Sometimes simple rules are better, even if they sometimes seem to make things inconvenient. File1.cpp int a; void f() { // Access a. // … } File2.cpp int a = 1; void g() { // Access a. // … } $ g++ File1.cpp File2.cpp ... Which one is the right one? Should it verify that all the same? Would it lead to hard to find bugs?

40 Include Guards The (loose) convention in C++ is to put each class in a separate header file. Is this correct? D1.hpp #include "B.hpp" class D1 : public B { … }; main.cpp #include "D1.hpp" #include "D2.hpp" int main() { D1 d1; D2 d2; // … } B.hpp class B { … }; Not right, because B will be define multiple times. D2.hpp #include "B.hpp" class D2 : public B { … };

41 Maintains simple rule: If you use a class, include its header file.
Include guards make includes “idempotent”. (This means it’s okay if a file gets included twice.) Maintains simple rule: If you use a class, include its header file. B.hpp #ifndef XYZZY_B_HPP #define XYZZY_B_HPP class B { … }; #endif D2.hpp #ifndef XYZZY_D2_HPP #define XYZZY_D2_HPP #include "B.hpp" class D2 : public B { … }; #endif D1.hpp #ifndef XYZZY_D1_HPP #define XYZZY_D1_HPP #include "B.hpp" class D1 : public B { … }; #endif main.cpp #include "D1.hpp" #include "D2.hpp" int main() { D1 d1; D2 d2; // … } The funny prefix is to prevent name clash with some other person or library that might use D1_HPP. Why the funny prefix?

42 Does this work? // A.hpp #ifndef ACME_A_HPP #define ACME_A_HPP #include “B.hpp” struct A { B *b_field; }; #endif // B.hpp #ifndef ACME_B_HPP #define ACME_B_HPP #include “A.hpp” struct B { A a_field; }; #endif There is a recursive include. We can break the recursion with a forward decl.

43 First-level of include:
// A.hpp #ifndef ACME_A_HPP #define ACME_A_HPP // B.hpp #ifndef ACME_B_HPP #define ACME_B_HPP #include “A.hpp” struct B { A a_field; }; #endif struct A { B *b_field; }; #endif Include of B.hpp from top-level A.hpp There is a recursive include. We can break the recursion with a forward decl.

44 Second-level of include
// A.hpp #ifndef ACME_A_HPP #define ACME_A_HPP // B.hpp #ifndef ACME_B_HPP #define ACME_B_HPP // A.hpp #ifndef ACME_A_HPP #define ACME_A_HPP #include “B.hpp” struct A { B *b_field; }; #endif struct B { A a_field; }; #endif struct A { B *b_field; }; #endif Include of B.hpp in top-level A.hpp Include of A.hpp in include of B.hpp in top-level A.hpp There is a recursive include. Most of the blue part is not actually included. We can break the recursion with a forward decl.

45 Second-level of include
// A.hpp #ifndef ACME_A_HPP #define ACME_A_HPP // B.hpp #ifndef ACME_B_HPP #define ACME_B_HPP // A.hpp #ifndef ACME_A_HPP #define ACME_A_HPP #ifndef ACME_B_HPP // Nothing here, all skipped #endif struct A { B *b_field; }; #endif struct B { A a_field; }; #endif struct A { B *b_field; }; #endif Include of B.hpp in top-level A.hpp Include of A.hpp in include of B.hpp in top-level A.hpp There is a recursive include. Most of the blue part is not actually included. We can break the recursion with a forward decl.

46 Solution is a forward declaration to break the cycle.
// A.hpp #ifndef ACME_A_HPP #define ACME_A_HPP struct B; struct A { B *b_field; }; #endif // B.hpp #ifndef ACME_B_HPP #define ACME_B_HPP #include “A.hpp” struct B { A a_field; }; #endif Sometimes, you will need to put them together into the same .hpp file.

47 How about this? // A.hpp #ifndef A_HPP #define A_HPP struct B; struct A { B foo() { return B(); } }; #endif // B.hpp #ifndef B_HPP #define B_HPP struct A; struct B { A foo() { return A(); } }; #endif Full definition required when function is defined, even if not used yet. If we don’t want inline, can use forward decl. Show recursive inline

48 Need to split apart class definition from function definition:
// A.hpp #ifndef MY_COMPONENT_HPP #define MY_COMPONENT_HPP struct B; struct A { inline B foo(); }; struct B { inline A foo(); }; inline B A::foo() { return B(); } inline A B::foo() { return A(); } #endif Show recursive inline

49 Can accomplish same effect by careful positioning.
// A.hpp #ifndef A_HPP #define A_HPP struct B; struct A { inline B foo(); }; #include “B.hpp” inline B A::foo() { return B(); } #endif // B.hpp #ifndef B_HPP #define B_HPP struct A; struct B { inline A foo(); }; #include “A.hpp” inline A B::foo() { return A(); } #endif Show recursive inline

50 Header files should be independent.
// Should not need anything here. #include <A.hpp> A header file should always be included in the implementation file. Which is better? // File A.cpp #include <iostream> #include <A.hpp> // Code is here… // File A.cpp #include <A.hpp> #include <iostream> // Code is here… If you put your header file at the very top of your .cpp, then it will help you catch any situation where your header file is no longer standalone.

51 CLASSES

52 Objects What is an object?
What is object-oriented programming? What is non-OOP? In non-OOP, data and the code that use and modify data are all mixed together. There is not a clear notion that this code goes with this data. Data is “amorphous”, without clear groupings. Can you do object-oriented programming in C? Xt File systems struct class1 { int (*method1)(struct class1 *, double x); double member1; int member2; }; p->method1(p, 3.14); Classes encapsulate operations and data, grouping them together. Class syntax is all about how to group the data and the operations, and what operations are valid, etc. Show include/linux/fs.h, show inode and operations vtable.

53 An object in C++ has a set of “fields”, known as data members
An object in C++ has a set of “fields”, known as data members. Those belong to the object. Then there are a set of member functions, these operate on the data members.

54 Class Definition Class definition looks like:
class Foo { … } obj1, obj2; class Foo { … }; Foo obj1, obj2; Are these different types? class Type1 { int mem1; double mem2; } o1; class Type2 { int mem1; double mem2; } o2; o1 = o2; // Type error? Types in C++ are by name only, so above doesn’t work. How about this? typedef Type1 Type3; Type1 o1; Type3 o2; o1 = o2; // Type error? Typedefs are aliases, so it works.

55 Another kind of typing, not supported by C++, is “duck typing”.
“When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck.” – James Whitcomb Riley If an object can behave like A, then it is an A. Usually used with dynamic typing. Can add methods at run-time. Just try to call the method. If it’s there, then it will succeed.

56 Member Variables Member variables (also known as data members or fields) are declared like: class A { string _label; int m_position; double energy_, weight_; }; Why all the funny stuff at the beginning or end? Sometimes people add _ at the end of member variables so that they are easily recognized as such in code.

57 Member Functions Member functions define the set of valid “operations” that you can do on an object of a particular class. class A { public: void op1(int i); void op2(double x) { … } void op3(const std::string &); … }; void A::op1(int i) { … } void A::op3(const std::string &s) { … } (Normally the function definition and class definition would not be in the same file.) Member functions always operate on an object. They have access to all member variables. Remember, you cannot call a member function without an object! (Except for static member functions, which we’ll talk about a bit later.)

58 Inside a member function, to refer to a member, just use the name:
class A { int member; … }; void A::func(int i) { int j = member*i; … } A a1, a2; a1.func(1); // Uses a1.member. a2.func(2); // Uses a2.member. What if a local variable has the same name as a member? class A { int i, j; … }; void A::func(int i) { int ii = i; int j = 2; int k = j; … } Local variables (and parameters) hide member variables.

59 Inlined member functions
Implicitly class A { void f() { … } Java-style. Explicitly class A { inline void f(); }; // Does this go in header file or .cpp file? inline void A::f() { … } Why inline? What should be inlined? Don’t get in the habit of always defining the member function in the class, since it will cause excessive inlining. Used to have an excessive inlining example, but can’t make it work anymore. Excessive inlining can cause bloat of executable, which can lead to making the inner loop of your program no longer fit in cache. Inline functions go in header files, since the compiler must see them in order to insert them inline.

60 Two (member) functions can have the same name
Two (member) functions can have the same name. As long as they have different signatures, there is no conflict. void foo(int); void foo(); void foo(int, int); int foo(int); foo(1); // ? (int) foo(1); // ? (double) foo(1); // ? (void) foo(1); // ? Cannot overload just on return type, so last line is a problem. You can’t even declare such a function.

61 The this Pointer Member functions execute “within” objects.
In the member function, a special keyword is used to refer to the object within which the member function is executing. class A { int member; void A::func(); }; void A::func() { // The type is as if defined as: // A *this; this->member; }

62 Inside a member function, class members are searched when a name is used.
class A { int a; … }; void A::func() { a = 1123; } This can be thought of an implicit usage of the this pointer, which can also be used explicitly for clarity or for disambiguation. class A { int a; … }; void A::func() { int a; a = 123; this->a = 456; }

63 Static Class Members Suppose you want to assign a unique object ID to every instance of a class. How would you do it? class A { int id; }; int obj_id; // Global counter. A::A() { id = obj_id++; } Disadvantages? Global variables should be avoided. How else would you improve the above? Make the id const, and use member initialization.

64 There is only one variable for each static member, which is used by all instances of a class.
Using a static member can enforce better encapsulation, avoid polluting namespace, etc. // In A.hpp. class A { private: const int id; static int next_id; }; // In A.cpp. A::A() : id(next_id++) { } int A::next_id;

65 Const static integer members can be assigned a value in the class:
struct A { static const int N = 10; }; They can be used as compile time constants: int array[A::N];

66 They do not require a definition unless there address is taken (odr-used).
#include <iostream> struct A { static const int N = 10; }; int main() { std::cout << &A::N << std::endl; } Won’t link. #include <iostream> struct A { static const int N = 10; }; int A::N; // Header file or implementation file? int main() { std::cout << &A::N << std::endl; } Okay.

67 Typedefs and Type Aliases
Typedefs and type aliases can be defined in a class. class A { public: typedef A *a_ptr; using b_ptr = B *; // C … }; … A::a_ptr a; // Pointer to A. A::b_ptr b; // Pointer to B. Type alias is the same as typedef except that with type alias can define alias templates. In this case, it is not that powerful, but we will see later on that typedefs can be very useful, especially when combined with templates.

68 Member Access Specifiers
C++ provides a way to label members with different degrees of access from scopes outside of the class. class A { int private_member; public: int public_member; private: int private_member2; }; public: All have access. private: Only member functions of the same class, or member functions of friend classes. protected: Only member functions of the same class, friend classes, or derived classes have access.

69 Somewhat misnamed: About access to names, not the actual entities:
References could provide access to objects. Type alias/defs to provide access to types. class A { int i; public: int &r = i; };

70 What is a struct? What is the difference between this class and this struct? class A1 { public: void f(); int i1; private: int i2; }; struct A2 { public: void f(); int i1; private: int i2; };

71 A struct is the same as a class, except that initial access is public instead of private.
class A1 { void f(); int i1; private: int i2; }; struct A2 { void f(); int i1; private: int i2; }; Private members Public members

72 Friends A function or operator can be granted access to private members. class A { friend void foo(); private: int i; }; void foo() { A a; a.i; // Okay. } void goo() { A a; a.i; // Access violation. } Member functions and other classes can also be friends. class A { friend int B::f(); friend class B; … };

73 A friend declaration is not actually a declaration:
A friend function can be defined in the friend declaration, and is implicitly inline: struct A { friend void foo() { } }; A friend declaration is not actually a declaration: struct B { friend void foo(); }; int main() { foo(); // Syntax error. } struct B { friend void foo(); void f() { foo(); } // Even this is a syntax error. }; However, in some cases, argument-dependent lookup (ADL) will apply. struct C { friend void foo(C *); }; int main() { C c; foo(&c); // Will compile, due to ADL. }

74 How Much Privacy Is There?
Is this breaking encapsulation? Is it how we think of encapsulation in the real world? class Person { private: int private_info; … }; void Person::snoop(Person *other_person) { cout << this->private_info << other_person->private_info << endl; } This is arguably breaking encapsulation, but is syntactically okay. What if you strongly are against this? What can you do? You can use conventions to try to enforce best practices. Coding style is that only the same object can access private members.

75 “Public” vs. public: I will sometimes use “public” in two different ways. “You don’t want this to be part of your public API.” Don’t document it as something for external use. “This member must have public: access for the code to work.” The first is a statement more at the design level. The second is at the implementation level. There is not always a perfect correspondence between the two levels.

76 What Do You Make Private?
Which classes do you make friend classes? class A { void somewhat_private(); void really_private(); void public(); }; Suppose class B needs A::somewhat_private(), while class C needs A::really_private(). What do you do? In C++, a class has: a “private” interface, composed of the data and function members exposed to friends and member functions. A “public” interface, composed of public members, exposed to everyone else. In real life, how many “interfaces” do you, as a person, have? In the real world, our relationships are not divided into just public and private. We have a completely public interface, a professional interface used at work, a friend interface used with friends, a family interface used with family, etc. If you have a member function that you want some other classes to have access to, but not all, there is no middle ground. You must choose either to make it private, and then the other class a friend, which means that the other class has access to all private functions, or you must make it public, which means that all classes have access to it.

77 Similarly, an object might have more than just two “interfaces”.
Suppose a class A had a helper class named Helper that needed access to a certain non-public interface, but not to all the private members of A. No way in current C++ to do this, but this is a possible syntax for it. class Helper {…}; class A { expose_to Helper: void func1(); expose_to all: // Same as public: void func2(); private: void func3(); Most of you will probably not be designing your own languages. Nonetheless, I think it is very helpful as users of a language to help understand how language design choices impact programming.

78 Forward Declarations Which of these are allowed?
class A { int i; B b; }; class B { int i; A a; }; class A { int i; B *bptr; }; class B { int i; A *aptr; }; Will the above actually compile? class A { int i; A *a; }; class A { int i; A a; }; class A { int i; static A a_member; }; class A { int i; A *a; // Okay with forward decl. }; class A { int i; A a; // Infinite recursive size. }; class A { int i; static A a_member; // Okay. }; class A { int i; B b; // Infinite. }; class B { int i; A a; // Infinite. }; class A { int i; B *bptr; // Okay with forward decl. }; class B { int i; A *aptr; };

79 Use a forward declaration.
class B; class A { B *bptr; }; class B { A *aptr; };

80 Classes Define a Scope One of the scopes in C++ is class scope.
// Where can you use these names? class A { int i; void foo(); }; Outside of a class, can use resolution operator. class A { … }; // This would refer to a member // of A. Will it compile? int i = A::var; Remember that scope is about what a name refers to. You can refer to a name in a way that is a syntax error. The names of class members can be used inside member functions.

81 Name resolution needs to consider class scope.
Local first, then class, then namespace.

82 const Member Functions
Objects (including primitives) in C++ can be declared const. const A a, b; const int i = 2134; i = 1; // Okay? a = b; // Okay? a.my_method(); // Okay? const A a, b; const int i = 2134; i = 1; // Not okay a = b; // No okay a.my_method(); // Okay only if my_method() is const.

83 Which of these will compile?
A member function can be called on a const object only if it does not change the object. C++ uses a keyword to indicate that the member function does not modify the object. What happens? void A::func() const { member = 1234; } Which of these will compile? void A::nonconst_func() { A *ap = this; } void A::const_func() const { A *ap = this; } void A::const_func2() const { const A *ap = this; } void some_func(A *obj); // Changes obj. void A::func() const { some_func(this); } void A::func() const { member = 1234; // Syntax error, since it tries to change a member. (Assuming that it is not marked mutable.) } void A::nonconst_func() { A *ap = this; } void A::const_func() const { A *ap = this; // The type of this is a const A *, so this is a type error. } void A::const_func2() const { const A *ap = this; // Okay. } void some_func(A *obj); // Changes obj. void A::func() const { some_func(this); // Syntax error, since you can’t pass a pointer to a const to a function that requires a pointer to a non-const. }

84 Let’s say that you really need to change a member in a const function
Let’s say that you really need to change a member in a const function. What do you do? Is this safe? void A::func() const { A *ptr = const_cast<A *>(this); ptr->member = 1234; } const A a_const; a_const.func(); // Okay? A a_nonconst; const A &const_ref(a_nonconst); const_ref.func(); // Okay? [Show cast_away_const.] Rule says that casting away constness is okay, but if you try to cast something that was defined const, the behavior is undefined. If you cast away constness of something that was not defined const, but is being accessed through a const pointer or ref, then it is okay.

85 What does constness mean?
void func(const A &a) { const int i1 = a.member; some_other_func(); const int i2 = a.member; if (i1 != i2) { // Is this possible? … } } Constness does not mean that the object will not change. It means that you promise not to change it.

86 A const member function is guaranteed not to modify the object.
Suppose you write a class named Trie, with a function: int lookup(const std::string &) const; Your users are not quite satisfied with speed. You examine the usage patterns, and you notice that it looks like: lookup “abcdef” … Repeated 1 million times. lookup “xyz” … Repeated 1 million times. etc. What might you do to improve performance?

87 Does this work? Should it work?
Cache the last lookup: class Trie { … std::string cached_key; int cached_value; }; int lookup(const std::string &key) const { if (key != cached_key) { cached_key = key; cache_value = real_lookup(key); } return cached_value; } Does this work? Should it work? This is a syntax error, because in a const member function, you can’t modify any of the members.

88 mutable keyword can be used to indicate that a member can be changed, even if the object is const.
class MyClass { mutable int var; }; Will allow this: struct A { void func() const; mutable int a_member; }; void A::func() const { this->a_member = 234; } int main() { const A const_a; const_a.func(); }

89 Const-ness can be physical or logical.
Physical constness: the bits don’t change. Logical constness: a user of the public interface of the object cannot detect any change. One is not necessarily better than the other, but keep these two aspects in mind.

90 Nested Types A number of things can be defined in class scope, and can be referenced from outside the class. struct A { int data_member; void func_member(); typedef int typedef_member; class class_member { … }; static int static_data_member; static void static_func_member(); enum enum_member { … }; }; Nested types can be used normally: A::typedef_member i; // Defines an int. // Defines an object of the nested class. A::class_member obj;

91 Nested Classes Classes can be nested: Okay, but what does it mean?
class A { class B { int i; void f(); }; void f(); int i; }; Okay, but what does it mean? The meaning is given in the following slides.

92 Suppose we create an object of type A
Suppose we create an object of type A. Is this the correct picture of the object? No, nesting a class only nests the scopes created by the classes. A a; class A { class B {…}; … }; Object of class of A::B

93 There is no nesting of objects implied.
A nested class can be used to create objects external to the outer class. class A { public: class B {…}; … }; A a_obj; A::B b_obj1; A::B b_obj2;

94 How can one get this, if this is what one wants?
Use composition. Or use inheritance. Object of class A class A { class B {…}; B b; }; Object of class of A::B

95 But this can be done without nested classes.
Object of class A class B {…}; class A { B b; … }; Object of class of B But this can be done without nested classes. What is the purpose then of nested classes? The scoping helps to hide information, and also reduce name clashes. With overloading, exceptions, and templates, often useful to create small helper classes, that are only used inside the outer class. These make sense to define as nested classes.

96 What about access and nested classes? Consider:
class A { class B { int B_priv; void B_f(); }; void A_f(); int A_priv; }; void A::A_f() { B b; b.B_priv; // Allowed? } void A::B::B_f() { A a; a.A_priv; // Allowed? } In C++98, neither is a friend of the other. A nested class is automatically a friend of the outer class (C++11). I.e., inner can access outer. The outer class is not a friend of the inner class, however. I.e., outer cannot access inner. Neither of the access are allowed.

97 Local Classes Can be declared in a function body.
void f() { class Local {…}; … } Everything must be in class definition. Sometimes handy for creating small, local functions.

98 Can call recursively, and access local static variables.
void work() { static int i; struct Local { static void foo() { goo(i + 1); } static void goo(int j) { } }; Local::foo(); }

99 Initialization, Assignment, and Destruction

100 Default vs. Implicit For some things, the compiler will automatically generate one for you, if you don’t define one. These are known as the implicit versions. What are some of these? Default constructor Destructor Copy constructor Copy assignment operator A constructor that takes no arguments is called the default constructor.

101 Constructors Objects are initialized with constructors:
class A { public: A(); // Default constructor. A(int); A(double, int, const B &); }; What gets executed? class A { public: A() { … } // Default ctor. A &operator=(const char *) { … } // Copy assign. }; A a = “hello”; These are essentially the same: A a(“hello”); // Direct initalization. A a = “hello”; // Copy initialization, NOT assignment. A a = “hello” is a syntax error because there is no constructor taking a string. A a = “hello”; is actually first constructing a temp using the string literal, then using the copy constructor. But commonly the copy construction is optimized out. But even though the copy ctor is optimized out, it is “conceptually” still executed, so if it the copy ctor is private, then there will be a syntax error. Since C++17, it’s no longer required in many cases that the constructor be accessible, so I don’t show equal_ctor any more.

102 Default constructor is used when no constructor arguments are given.
A a1, a2(3), a3(); What is the difference between a1 and a3? Hint: A a4(int); Default arguments can be useful: Time::Time(int hour, int min, Timezone tz = UTC); a1 calls default ctor, a2 is a function, a3 calls ctor that takes a single int.

103 Which of these is okay? class A { public: A(int); }; class B { }; B b; // Compiles? A a; // Compiles? If you don’t define any constructor, the compiler will implicitly define a default constructor. If you define a non-default constructor, then the compiler will not implicitly define the default constructor. B b; compiles, because it uses the default constructor. A a; does not compile, because no default constructor will be implicitly defined.

104 What happens if you don’t do the above? Any difference?
You can explicitly ask the compiler to generate the default constructor in C++11: struct Foo { Foo() = default; // Inlined. std::string str; }; struct Foo { Foo(); std::string str; }; // In .cpp file. Foo::Foo() = default; // Not inlined. What happens if you don’t do the above? Any difference? What about this? struct Foo { Foo() = default; Foo(const char *s) : str(s) {} std::string str; };

105 Conversions How would you critique this code?
struct String { // Construct using given C string. String(const char *s) { } // Create a string of spaces of the given length. String(int length_) { } int length; char *buf; }; int nl = 100; // Maximum length of names, used later. void print(const String &) { cout << s.buf << endl; } ... // Create three names. String n1(“alice”), n2(“bob”), n3(“joe”); print(nl); // Print the first name. A constructor will be used by the compiler as a conversion. It can cause unexpected results.

106 struct Polygon { Polygon(int n_sides);. }; void draw(const Polygon &);
struct Polygon { Polygon(int n_sides); }; void draw(const Polygon &); ... Polygon square(4); int squared = 4*4; // Area of the square. // Draw the square created above. draw(squared); A constructor will be used by the compiler as a conversion. It can cause unexpected results.

107 The programmer most likely did not intend the automatic conversion.
How to prevent it? Constructors define conversions. What if you don’t want the conversion? Disable with explicit keyword. For example, explicit Polygon(int n); class String { public: // Create a string of spaces // of the given length explicit String(int length); … }; Explicit constructors also cannot be used in copy initialization: Polygon p1(10); // Okay. Polygon p2 = 10; // Error if explicit.

108 Explicit Temporaries Let’s say you have a 2-D Vector class:
Vector v1(x1, y1), v2(x2, y2); v1 = v2; v2 = ...; // How to assign (2.3, 3.1) to v2? Could use a “setter” method. v2.set(2.3, 3.1); The more idiomatic C++ way is with explicit temporary. v2 = Vector(2.3, 3.1); Also very commonly used with return values. Vector operator+(const Vector &v1, const Vector &v2) { return Vector(v1.x + v2.x, v1.y + v2.y); }

109 Member Initialization
Consider a class designed to be a better string: class String { … int len; // Length of the string char *buf; // Array of chars to hold it. }; What should the initial values be, when we create a string? How do the members get the initial values? [Show string_init.] Initial value of len should be 0, most likely. Initial value of buf is probably pointer to array of len 1, to hold the null character. The point is that there is a right set of initial values, and that the constructor is what sets those.

110 Consider: struct A {…}; struct B { B(); A a; }; B::B() { // How do we initialize the a member? } If we do nothing, then the default constructor for A will be used, if there is one. If default constructor for A is not right, then use member initializer list: B::B() : a(12, 3) {}

111 Any difference between the two versions of the constructor?
class A { public: A(const char *name); private: std::string name_; }; // Version 1. A::A(const char *name) { name_ = name; } // Version 2. A::A(const char *name) : name_(name) {} In the version using an assignment in the body of the constructor, the name_ member is first constructed using the default constructor. The assignment operator is then called. This can be inefficient. In the version using the member initializer, the name_ member is directly constructed using the const char *. This is more efficient.

112 Any time when you must use initializer list syntax?
struct B { B(int); }; class A { public: A(const char *); private: const std::string name_; B b; }; A::A(const char *name) : b(1) { name_ = name; // Okay? } Must use A() : name_(“foo”) syntax, because name_ is const, and cannot be assigned to.

113 Construction of const object
Is this okay? struct A { A() { member = 1234; // Okay? } int member; }; // Assigns to member inside ctor. const A a; Inside the constructor of a const object, the this pointer is not const, so we can assign to members. Makes sense in hindsight, since this must be statically typed.

114 Default Member Initializers
Does this work? class A { int position = 2; }; No in C++98, yes in C++11. Also can do: class B { vector<int> v = {1, 2, 3}; vector<int> v2{4, 5, 6}; int i = some_func(); };

115 Gives a default that all constructors use.
Any advantage? Gives a default that all constructors use. class A { A(int i) {} A() = default; int position = 2; }; Order is the same as listed in class. class B { public: // Using var2 before it’s initialized. B() : var1(var2) {} private: int var1; int var2 = some_func(); };

116 Constraining Object Creation
What if we don’t want to allow an object of a particular class to be created just anywhere? class A {…}; void foo() { A a; // How to disallow this? } Use private, unimplemented constructor (C++98): class A { private: A(); }; How is the object ever created then? Can be created in friends, static member functions, etc. Can be created in friends, static member functions, etc.

117 In C++11, you can make this explicit with the delete keyword.
Note that if you define a private, non-default ctor, the default ctor will not be generated: class A { private: A(int); }; In C++11, you can make this explicit with the delete keyword. class A { A() = delete; private: A(int); }; Can be created in friends, static member functions, etc.

118 Copy Constructors Consider the following:
void foo(A a_param) { … } A a_arg(2); foo(a_arg); Is a_param the same object as a_arg? If not, then how is it initialized? What function is called? Initialization of an object with another object is done so often it has a special name: copy construction. The prototype is: A(const A &). Could also be: A(A &), but most likely not a good idea. Cannot be: A(A a) (Why?)

119 If you don’t define a copy constructor, one will be automatically defined (implicit), that has the following definition. class A { B1 b1; B2 b2; int i; }; A::A(const A &a) : b1(a.b1), b2(a.b2), i(a.i) {}

120 Consider a string class.
class String { public: String(); ~String(); private: char *buf; }; String::String() : buf(malloc(10)) { strcpy(buf, “hello”); } String::~String() { free(buf); } Okay? Does the implicit copy constructor do the right thing for this class? Implicit copy constructor does not do the right thing, because it does a “shallow” copy.

121 In C++11, use delete keyword.
If the implicit copy constructor is not correct, to disallow copy construction, use a private, undefined copy constructor. class A { private: A(const A &); // Not defined. … }; In C++11, use delete keyword. class A { A(const A &) = delete; … };

122 Delegating Constructors
Let’s say you have a class that needs to be initialized with a string and an integer. A a(“hello”, 5); struct A { A(const std::string &s, int i) { /* … */ } // … }; You would like to make the integer default to 1 if not supplied. A a(“hello”); struct A { A(const std::string &s, int i = 1) { /* … */ } // … }; Also, you’d like to make the string default to “hello” if not supplied. A a(5); struct A { A(const std::string &s = “hello”, int i) { /* … */ } // … }; A(const std::string &s = “hello”, int i) { /* … */ } A(int i) { … }

123 You could also use a constructor helper:
struct A { A(int i) { ctor_helper(“hello”, i); } A(const std::string &s, int i = 1) { ctor_helper(s, i); } private: void ctor_helper(const std::string &, int i); // … }; Members are constructed, then assigned to, which is inefficient. Doesn’t work if there are const members. struct A { A(int i) : count(i) { ctor_helper(“hello”, i); } private: void ctor_helper(const std::string &, int i); const std::string s; const int count; // … };

124 No great solution in C++98. In C++11, use delegating ctors:
struct A { A(const std::string &n, int c) : name(n), count(c) { /* … */ } A(int c) : A(“hello”, c) { /* … */ } A(const std::string &n) : A(n, 1) { /* … */ } private: const std::string name; const int count; // … };

125 Uniform Initialization
C++98: Two forms of initialization: // A1 is an aggregate. struct A1 { int i, j; } a1 = {1, 2}; struct A2 { A2(int i, int j) : i(i), j(j) {} int i, j; } a2(1, 2); Aggregate initialization inherited from C. Confusing and annoying.

126 Uniform initialization (C++11): When initializing something, always use curly braces.
struct A2 { A2(int i, int j) : i{i}, j{j} {} } a2{1, 2}; Calls the constructor, or does aggregate initialization.

127 Summary: // An aggregate. struct A { int i; const char *s; }; A a1 = {1, “hello”}, a2{2, “goodbye”}; // Not an aggregate. struct B { int i; private: const char *s; }; B b{1, “hello”}; // Compile-time error. // Not an aggregate, but has a ctor. struct C { C(int i_, const char *s_) : i(i_), s(s_) {} private: int i; const char *s; }; C c{1, “hello”}; // Okay. The entire object could be “pre-initialized” at compile time, and placed in the data segment. Note that with the private member, it’s simply not an aggregate any more. It’s not that the access is private.

128 Also, no way to statically initialize a non-array container in C++98.
int a[] = {1, 2}; // Works. std::vector<int> v = {1, 2}; // No.

129 In C++11, the compiler will turn an initializer list into a std::initializer_list<T>.
// Uses std::initializer_list. struct D { D(int, int); // C1 D(int, double); // C2 D(std::initializer_list<int> il); // C3 … }; D d{1, 2, 3}; D d2{1, 2}; // Prefers initializer_list ctor. D d3{1, 2.0}; // Calls C2. D d4{1, 2.0, 3}; // Error.

130 Destructors Is this correct? Memory leak, to fix:
struct A { A(); char *buf; }; A::A() : buf(malloc(10)) {} Memory leak, to fix: struct A { A(); ~A(); char *buf; }; A::A() : buf(malloc(10)) {} A::~A() { free(buf); buf = 0; } Necessary? The buf=0 is not necessary, but can help to catch uses of buf after it has been freed.

131 Clean up when an object goes out of scope.
What kind of things might it do? It might close files, unmap files, free memory, destroy mutexes, shared memory, file locks

132 Assignment (Copy) Assignment operator is called:
class A {…}; A a, b; a = b; If no (copy) assignment operator is defined explicitly, an implicit one will be defined by the compiler. It will be equivalent to: class A { B b, c; int i; }; A &A::operator=(const A &a) { b = a.b; c = a.c; i = a.i; return *this; }

133 Consider our string class again.
class String { public: String(const char *s); ~String(); private: char *buf; int len; // Length not including null byte. }; String(const char *s) : len(strlen(s)) { buf = new char[len + 1]; strcpy(buf, s); } ~String() { delete [] buf; } Would the implicit assignment operator be correct for this? To disallow, make it private and undefined. Or use delete keyword. Implicit assignment operator would not be correct. buf would be copied, resulting in two String objects sharing a buf. When the destructors are called, both objects will try to delete buf, resulting in a double delete.

134 What should the return type of the assignment operator be?
??? MyType::operator=(const MyType &); Should this work? a = b = c; // Which way does this group? a = (b = c); // Can it return a value? For efficiency, it should return a reference. Can it be a const reference? What about this? void f(MyType &); f(a = b); // Should this work? Best practice is to make assignment operator return a & to the object being assigned to. This makes it most similar to assignment on primitive types.

135 Value Return of Classes
What gets executed here? class A { public: A(int); }; class B { public: B(const A &); }; A f() { return 1; } B b(f()); Semantics same as initialization. Executes A(int) to create an A, then copy constructor, then B(const A &) to make a B. Some copy constructors may be optimized away.

136 Rule of Three If you have to define the destructor, copy ctor, or assignment operator, then you usually have to define all three.

137 Operators and Conversions

138 Overloading Operators can also be overloaded.
Complex c1(…), c2(…), c3; c3 = c1 + c2; There are complicated rules for figuring out which overloaded function to call. Why overload? Sometimes a particular operation, like finding the maximum of two numbers, can have different implementations depending on the actual types.

139 Operators Consider these operators defined in the context of two classes: class Point { private: double x, y; }; class Vector { private: double x, y; }; These classes have exactly the same data members. Is there any point in having two classes? Why not just make them the same? class Pair { private: double x, y; }; Just because the data is the same, and some of the operations overlap does not mean that you should merge two classes into one.

140 Does this code do what the author intended?
Pair s1p(…); // Position of starship 1. Pair s1v(…); // Velocity of starship 1. double dt = …; // Elapsed time. // Update starship position, assuming // that dt seconds have passed since the // last update. s1p = s1p + dt*s1p; If the author had used separate classes for points and vectors, would it have made any difference? v1p = v1p + dt*v1p; should have been v1p = v1p + dt*v1v; If the author had used separate classes, she would have gotten a syntax error.

141 Equality (==) Equality
inline auto operator==(const Point &p1, const Point &p2) { return p1.x == p2.x && p1.y == p2.y; }

142 Inequality (!=) Inequality:
inline bool operator!=(const Point &p1, const Point &p2) { return !(p1 == p2); }

143 Addition (+) Point + point? Point + double? Point + vector?
Ill-defined. Point + double? Point + vector? auto operator+(const Point &p, const Vector &v) { return Point(p.x + v.x, p.y + v.y); } Vector + point? inline Point operator+(const Vector &v, const Point &p) { return p + v; }

144 Multiplication (*) Point*point? Point*double? double*vector?
Ill-defined. Point*double? double*vector? inline Vector operator*(const double s, const Vector &v) { return Vector(s*v.x, s*v.y); } vector*double? inline Vector operator*(const Vector &v, const double s) { return s*v; } vector*vector? What do we do about cross-product? Can we just “borrow” some other operator to use for cross product, such as &&? Vector &operator&&(const Vector &v1, const Vector &v2); You can borrow other operators, but the precedence might be unexpected.

145 Full declaration Code is:
class Vector; class Point; Point operator+(const Point &, const Vector &); class Vector { friend Point operator+(const Point &, const Vector &); … }; class Point { friend Point operator+(const Point &, const Vector &); … };

146 Index operator ([]) Index
class DynamicArray { public: double &operator[](size_t i); double operator[](size_t i) const; private: double *const data; const size_t length; }; double & DynamicArray::operator[](size_t i) { return data[i]; } Can put a range check in index operator.

147 Function call (()) Functor: Object that behaves like a function.
class Compare { public: bool operator()(int i, int j) { return i < j; } }; Compare cmp; if (cmp(1, 2)) { … } // If <. In C++, use functors (with virtual functions if needed) where you would normally use function pointers.

148 Also can do double-duty as a multi-dimensional index operator:
class Array2 { public: double &operator()(int i, int j) { return data[???]; } private: size_t n_cols; double *data; }; return data[n_cols*i + j];

149 Member Access and Dereference (-> and *)
These are somewhat special. They allow a class to behave like a pointer. Is there a way to solve memory leak problems like below by using objects? { MyClass *ptr = new MyClass; ptr->my_method(); // Oops, memory leak. }

150 Use a “smart pointer”: class MyClassPtr { public: MyClassPtr(MyClass *p) : ptr(p) {} ~MyClassPtr() { delete ptr; ptr = 0; } MyClass *operator->() { return ptr; } MyClass &operator*() { return *ptr; } private: MyClass *ptr; }; { MyClassPtr sp(new MyClass); sp->my_method(); (*sp).my_method(); // What happens here? } This is not a robust version, just for example. It is similar std::auto_ptr.

151 If the member access returns an object type, it will recurse:
struct A { int field1; }; struct B { A *operator->() { return &a; } A a; }; struct C { B operator->() { return B(); } }; C c_obj = . . .; c_obj->field1; (C_obj.operator->().operator->())->field1;

152 Left and Right Shift (<< and >>)
These are used primarily for output operators: void operator<<(ostream &os, const Point &p) { os << "(" << p.x << ", " << p.y << ", " << p.z << ")"; } Given the above, will this work? Point p1(…), p2(…); cout << p1 << p2 << endl; Return type needs to be changed to ostream &.

153 Assignments (*=) How do we make this work?
Vector v(…); double s = …; v *= s; // What does this do, mathematically? Vector &operator*=(double s) { this->x *= s; this->y *= s; return *this; }

154 Increment (++) and Decrement (--) Operators
What is the value of i in these cases? int j = 3; int i = ++j; int j = 3; int i = j++; The postfix and prefix operators are distinguished with a hack. operator++(int); // Postfix operator++(); // Prefix

155 How would you implement 128-bit unsigned integers?
Int128 i, j1(1), j2(1); i = j1++; // What should the value of i be? i = ++j2; // What should the value of i be? class Int128 { public: Int128(uint64_t h = 0, uint64_t l = 0) : m_high(h), m_low(l) {} ??? operator++() { // Prefix if (++m_low == 0) { m_high++; } return ???; } ??? operator++(int) { // Postfix if (++m_low == 0) { m_high++; } return ???; } private: uint64_t m_high, m_low; };

156 class Int128 { public: Int128(uint64_t h = 0, uint64_t l = 0) : m_high(h), m_low(l) {} inline Int128 &operator++(); // Prefix inline Int128 operator++(int); // Postfix private: uint64_t m_high, m_low; }; inline Int128 & Int128::operator++() { //Prefix if (++m_low == 0) { m_high++; } return *this; } inline Int128 Int128::operator++(int) { // Postfix uint64_t org_high = m_high, org_low = m_low; this->operator++(); return Int128(org_high, org_low); }

157 Which is more efficient? How many temporaries are created?
Int128 &Int128::operator++() { //Prefix if (++m_low == 0) { m_high++; } return *this; } Int128 Int128::operator++(int) { // Postfix uint64_t org_high = m_high, org_low = m_low; this->operator++(); return Int128(org_high, org_low); } Which is more efficient? How many temporaries are created? Int128 i, j1(1), j2(1); i = j1++; // What should the value of i be? i = ++j2; // What should the value of i be? Preincrement is more efficient because it doesn’t create temporaries. Postincrement creates at least one.

158 User-Defined Conversions
Suppose we have our Int128 class. class Int128 { … }; We’d like to use some function with this prototype: void foo(Int128 &); Can we do this? ... foo(1234); Yes, since there is a constructor: Int128::Int128(uint64_t h = 0, uint64_t l = 0); Constructor default arguments are actually wrong. Order should be reversed.

159 Must be const or rvalue reference.
So, like this, right? void foo(Int128 &); … foo(1234); Hm…what happens here? void foo(Int128 &i) { i = j; } ... foo(1234); Must be const or rvalue reference. void foo(const Int128 &); ... foo(1234); void foo(Int128 &&); … foo(5678); Principle is that a non-const ref to a temporary is not allowed.

160 So we defined a conversion from int to Int128. Can we go the other way?
void foo2(int); Int128 i; ... foo2(i); Yes. class Int128 { public: operator int() const; }; Int128::operator int() const { // Convert to int. return result; }

161 Any conversion is possible.
class A { public: const operator *my_struct(); operator double(); private: my_struct a_struct; }; const A::operator *my_struct() { this->a_struct.field1 = 1.234; this->a_struct.field2 = 4.567; return &this->a_struct; } A::operator double() { return 3.14; } A a(…); void f1(double); void f2(const mystruct *); f1(a); f2(a);

162 To force the use of a cast, use the explicit keyword.
class A { public: explicit const operator *my_struct(); operator double(); private: my_struct a_struct; }; A a(…); void f1(double); void f2(const mystruct *); f1(a); f2(a); // Error. f2(static_cast<const my_struct *>(a)); // OK.

163 Especially important with a bool conversion.
struct A { operator bool() { ... } }; ... A a; int i = a; // Unintended, but compiles and runs. ... struct B { explicit operator bool() { ... } }; int j = b; // Gives compile-time error. ... if (a) { ... } // Works. // Also works, because it’s a boolean context. if (b) { ... } Conversion is still implicit if in boolean context. Replaces the “safe bool idiom” in C++98.

164 Member or friend? Member or (friend/non-friend) free?
Vector v1, v2; v1 = v2*s; Vector operator*(const Vector &, double); Vector Vector::operator*(double) const; v1 = s*v2; // ? Consider a complex number example. class Complex { Complex(double real, double img = 0); Complex operator*(const Complex &); }; Complex c1 = 4*c2; // Syntax error. Complex operator*(const Complex &, const Complex &); class Complex { Complex(double real, double img = 0); }; Complex c1 = 4*c2; // OK. No one answer, but usually, the conventional wisdom is that binary operators should be defined as free, non-friends.

165 Temporaries Consider the addition operator for vectors:
Vector operator+(const Vector &v1, const Vector &v2) { return Vector(v1.x + v2.x, v1.y + v2.y); } In the expression below, how many temporaries are generated? v = v1 + v2 + v3; Any solution? Not in C++98, but move semantics in C++11 can help. Go back to this for move constructors. At least two temporaries are generated. There is no good solution to this.

166 What is the lifetime of a temporary?
struct A { A(int); void method(); }; void foo1(const A &); A foo2(); foo1(1); // A ref is passed to foo(). foo2().method(); // Call a method on temp? const A &ar(foo2()); ar.method(); // Okay?? Lifetime is generally to the end of the full expression. Binding a ref to a temp will extend the lifetime.

167 Summary Cannot be overloaded: Must be member function:
?: (conditional) . (member selection) .* (member selection with pointer-to-member) :: (scope resolution) sizeof (object size information) typeid (object type information) Cast operators: (type), static_cast, dynamic_cast, reinterpret_cast, const_cast Must be member function: = (assignment) () (function call) [] (subscripting) -> (member access)

168 If something already has a clearly defined meaning, then you can’t overload it.
int operator&&(int, int); // Okay? int operator&&(MyClass a, int); // Okay? int operator&&(MyClass *ap, int); // Okay? int operator&&(MyClass &a, int); // Okay? enum Foo { RED, GREEN, BLUE }; int operator&&(Foo, int); // Okay? All okay except: int operator&&(int, int); // Okay? int operator&&(MyClass *ap, int); // Okay? Basically, must be class or enumerated type.

169 Special Values Suppose you had a smart pointer class. How would you check it for being null? Suppose you had a time class. How would you get the current time? [Show special_ values]

170 Comparison against zero could use a regular object with zero value:
class Time { static Time zero; }; if (t == Time::zero) {…} Or it could use a tag struct. In this case, any object of the type means zero. class ZeroTime {} zero_time; if (t == zero_time) { ... } Or could overload comparison to int: if (t == 0) { ... } But in this case it would still be called, even if it was 1234 instead of 0. Can get now time by a static function. Time t = Time::now(); Or can create a set-to-now member function: t.setToNow();

171 Inheritance

172 Inheritance A class may be derived from (inherit from) another class. The class that is derived from is the base class. Which is more general, the base class or the derived class? Why inherit? What do we gain by adding inheritance to a language? What would we lose if we took it out? Main thing is layered abstraction. Consider a hierarchy of animals, including felines, canines, and mammals. Code that doesn’t care if it is a dog or cat can deal with just mammals. Inheritance defines what we call the Is-A relationship. Base class is more general. Mammal Cat Dog

173 Open-Closed Principle
Software should be open for extension, closed to modification. Hard to accomplish in practice.

174 Liskov Substitution Principle (LSP)
If D is a subtype of B, - then objects of type B can be replaced everywhere with objects of type D, - without impacting any of the desired properties of the program. In other words, if D derives from B, you should be able to replace objects of type B everywhere with objects of type D, and nothing will “go wrong”.

175 So using derived type object should still work.
void do_something(B *); B b; do_something(&b); D d; // D derives from B. do_something(&d);

176 A rectangle has four lines, each angle is 90 degrees.
A square has four lines, each of the same length. Each angle is 90 degrees. Is a square a rectangle? Then to model this, we should derive square from rectangle: class Square : public Rectangle { // … }; Should this work? Rectangle rect; Square sq; void do_something(Rectangle *r); do_something(&rect); do_something(&sq); Substituting a square for a rectangle should work, if a square is-a rectangle.

177 Consider: class Rectangle { public: void setWidth(double x); void setHeight(double y); // ... }; void do_something(Rectangle *r) { r->setWidth(10); r->setHeight(5); // ... } Rectangle rect; do_something(&rect); class Square : public Rectangle { /* ... */ }; Square sq; do_something(&sq);

178 Ways to resolve: Changing either width or height of a square changes the other. A Square object is not a Rectangle object. Don’t use mutators. (Use a FP style.) Allow object to change type.

179 Flat or Deep? Should a checking account and a savings account use separate classes or the same classes? Checking is positive fee, zero interest. Savings is zero fee, non-negative interest. Student and staff? Generally flat is preferred. This is clearly a design decision, and there is no single right answer. It’s a tradeoff between having functionality “built-in”, and having it parameterized in some way.

180 Defining the Hierarchy
To inherit: class B { // Accessible to derived classes. protected: int i; private: int j; }; class A : public B { … }; void A::some_func() { i; // Allowed. j; // Error. }

181 To prevent inheritance, use final keyword (C++11).
class A final { /* … */ }; class B : public A {// Syntax error. /* … */ }; Can also have protected and private inheritance. class Derived : protected Base { /* … */ }; class Derived : private Base { /* … */ };

182 Sub-objects An instance of derived class contains the members from the base class as what we call subobjects. class A {…}; class B : public A {…}; class C : public B {…}; A part B part C part

183 Inheriting A derived class can access data members in base classes, if given protected access. A member function defined in a base class is automatically inherited by the derived class. class Base { public: void func(); }; class Derived : public Base {}; Derived d; d.func(); // Calls Base::func().

184 Name Hiding Consider: class B { public: void func(); }; class A : public B { public: void func(); }; A a; a.func(); // Calls which? A function that is defined in the derived class hides the same function defined in the base class. How to call the B version? // Use resolution operator to call base class. a.B::func();

185 Which ones are called? struct Base { void func(double); void func(int); }; struct Derived : public Base { void func(int); }; Derived d; d.func(1234); // Which does this call? d.func(12.34); // Which does this call? Both call the Derived class function, due to hiding.

186 Which ones are called? struct Base { void func(double); void func(int); }; struct Derived : public Base { void func(int); private: void func(double); }; Derived d; d.func(12.34); // Which does this call? Resolution happens before access check.

187 Note that the derived class still gets precedence if declared in both.
Normally, there is no overloading of hidden names. Can get this via “using”. struct Base { void func(double); void func(int); void func(const char *); }; struct Derived : public Base { using Base::func; void func(int); private: void func(const char *); }; Derived d; d.func(1234); // Which does this call? d.func(1.23); // Which does this call? d.func(“hello”); Note that the derived class still gets precedence if declared in both. d.func(1234); calls Derived::func(int) d.func(1.23); calls Base::func(double) d.func(“hello”) is a syntax error (can’t call private func). The ones in the derived class still get precedence.

188 All base overloads must be accessible to derived class.
struct Base { void func(double); protected: void func(); private: void func(int); }; struct Derived : public Base { using Base::func; void func(int); }; This is a syntax error. A protected base class member can be made public in the derived class, however.

189 Access is determined by the point at which the using declaration occurs:
struct Base { void func(double); protected: void func(); }; struct Derived : public Base { private: using Base::func; public: void func(int); }; derived.func(1.1); // Compile-time error.

190 Construction A derived object has a sub-object that is a instance of the base class. When does the sub-object constructed? Which should be constructed first? Generally speaking, it makes more sense to construct the base class first. class Base { protected: Base(); }; class Derived : public Base { public: Derived(); }; Derived d; Sub-object is constructed by base class constructor. Base class should be constructed first.

191 Consider: Will it compile?
class Base { protected: Base(int); }; class Derived : public Base { public: Derived() { … } }; Will it compile? Will not compile, since the Base class has only one constructor, and it takes an int. So the Derived class constructor must pass an int to it.

192 Calling constructor of base classes.
If we wish to call a non-default constructor, use a constructor initialization list: class B { public: B(int); }; class Derived : public B { public: Derived(); private: B b; }; Derived::Derived() : B{123}, b{456} {}

193 struct A { A(int, int); A(const char
struct A { A(int, int); A(const char *); }; struct B : public A { B(); A a; }; B::B() : A{1, 2}, a{“a”} {…}

194 Copy construction doesn’t “propagate” up:
struct Base { Base(); Base(const Base &); }; struct Derived : public Base { Derived(const Derived &) {} } Which base class ctor is called? Calls Base::Base().

195 Inheriting Constructors (C++11)
Constructors can be inherited in C++11. struct Base { Base(int); }; struct Derived : public Base { using Base::Base; }; Derived d{1};

196 Destructors Destructors of base classes are called.
What order should destructors be called? class Base { public: ~Base(); }; class Derived : public Base { public: ~Derived(); }; They are called in reverse of ctors, so most derived is called first.

197 Polymorphism What does the word literally mean?
What does it mean in terms of C++? It means that you have a virtual function.

198 Virtual Functions A virtual function is one that when you call it through a pointer or a reference to a base class, calls the implementation in the most derived class. class Base { public: virtual void foo() { printf(“Base\n”); } }; class Derived : public Base { public: virtual void foo() { printf(“Derived\n”); } }; Derived d; Base b; Base &br_d{d}, &br_b{b}, *bp = 0; bp = &b; bp->foo(); bp = &d; bp->foo(); br_d.foo(); br_b.foo();

199 Do we need the virtual in the derived class?
class Base { public: virtual void foo1(); void foo2(); }; class Derived : public Base { public: /* virtual */ void foo1(); void foo2(); } Virtual-ness propagates down the class hierarchy, so no need for virtual keyword in the derived class, but some people put it there as a reminder.

200 Which functions get called?
class A { public: void foo(); }; class B : public A { public: virtual void foo(); }; class C : public B { public: virtual void foo(); }; C c; A *ap = &c; B *bp = &c; ap->foo(); bp->foo(); bp->A::foo(); c.B::foo(); Virtual functions ‘start’ at the level where they are first declared virtual. ap->foo(); // Calls A::foo(). bp->foo(); // Calls C::foo(). bp->A::foo(); // Calls A::foo(). c.B::foo(); // Calls B::foo().

201 What happens here? class Base { public: virtual void foo(); }; class Derived : public Base { private: void foo(); }; Derived d; Base &br(d); br.foo(); As a thought experiment, let’s say that we wanted to disallow this. Is this practical/wise? An override can be private.

202 Not all overloads of a function must be virtual:
class Base { public: virtual void foo(int); void foo(double); void foo(const char *); }; class Derived : public Base { public: void foo(int); void foo(double); void foo(); }; Derived d; Base &br(d); br.foo(1); br.foo(1.1); br.foo(“hello”); br.foo(); d.foo(1); d.foo(1.1); d.foo(); d.foo(“hello”); Such code is unlikely to be a good idea, but it may happen unintentionally.

203 Virtual and using declaration
using declarations still result in dynamic dispatch. struct Base { virtual void func(double); }; struct Derived1 : public Base { private: virtual void func(double); }; struct Derived2 : public Derived1 { void func(int); }; int main() { Derived2 d2; d2.func(1.0); } using Derived1::func; using Base::func; With nothing, it calls Derived2::func, with using Base::func, it calls Derived1. With using Derived1::func, it’s a syntax error, since Derived1::func is not accessible.

204 Operators Can Be Virtual
Recall that operators are just functions. So they can be virtual: struct Log { virtual Log &operator<<(const char *); }; struct RemoteLog : public Log { virtual Log &operator<<(const char *) override; }; void emergency(Log &l) { l << “Computer on fire!”; }

205 Forcing/Preventing Overriding (C++11)
To flag an error when you intend to override, but actually aren’t, use the override keyword. struct Base { virtual void foo(int); }; struct Derived : public Base { virtual void foo(double) override; }; Without override, the above would compile. To prevent overriding, use the final keyword. struct Base { virtual void foo(int) final; void goo() final; }; struct Derived : public Base { virtual void foo(int); // Syntax error. void goo(); // Syntax error. };

206 Virtual Destructors Will this do the right thing?
Base *b = new Derived; delete b; To cause the destructor for the derived class to be called, use a virtual destructor: class Base { public: virtual ~Base() {} }; You almost always want a virtual destructor if you have a polymorphic class. delete b will not do the right thing unless the destructor is declared virtual. If not virtual, then it will not call the derived class constructor.

207 Default Arguments What default arguments will be used?
class Base { public: virtual void foo(int i = 0); }; class Derived : public Base { public: virtual void foo(int i = 1); }; Derived d; Base *b = &d; b->foo(); // Will this pass one or zero? Default arguments are done statically, at compile time, so will not use polymorphism. So b->foo() will pass 0. Using different default arguments is probably a bad practice anyway. I think it is more likely to happen accidentally.

208 Parameter-Based Virtual Dispatch
Which output operator does this call? class Base { public: virtual ~Base(); … }; ostream &operator<<(ostream &os, const Base &) { // Output for Base class. } class D : public Base { … }; ostream &operator<<(ostream &os, const D &) { // Output for D class. } D d; Base b, &br_d(d); cout << b; // Which does this call? cout << d; // Which does this call? cout << br_d; // Which does this call? cout << b; // Calls Base version. cout << d1; // Calls the D1 version. cout << b1; // Also calls Base version.

209 Problem is that C++ does virtual dispatch only on the object type of the member function, not on any parameters. Solution? [param_dispatch/] Turn it into a member function call.

210 Redirected dispatch. ostream &operator<<(ostream &os, const Base &b) { b.print(os); return os; } class Base { virtual void print(ostream &os); … }; class D : public Base { virtual void print(ostream &os); … }; D d; B b, &br_d(d); cout << b; // Which does this call? cout << br_d; // Which does this call? Now calls the right print() version.

211 Constructors and Virtual Functions
What does this call? class Base { Base(); virtual void helper(); }; class Derived : public Base { virtual void helper(); }; Base::Base() { // Does this call Derived::helper(), or // Base::helper()? this->helper(); … } Hint: At the point of call, has the constructor for Derived been called yet? Is it safe to call a method on an object that has not been constructed? this->helper() calls Base::helper(). It is not safe to call a method for which the constructor has not yet been entered. However, the compiler prevents this from happening. It is okay to call a method on an object whose constructor body has not yet exited (as long as you are careful).

212 Pure Virtual Functions
A base class can indicate that the class of any instantiated object must override the function. struct Base { virtual void f() = 0; }; struct D1 : public Base { … }; struct D2 : public D1 { virtual void f(); } struct D3 : public D2 { … }; Base b; // Okay? D1 d1; // Okay? D2 d2; // Okay? D3 d3; // Okay? Called an abstract class. Base b; // No. D1 d1; // No. D2 d2; // Okay, because it overrides f(). D3 d3; // Okay, because it inherits from D2, which overrides f().

213 Used to provide helper functions.
Is this allowed? What does it mean? Could the implementation ever be called? struct Base { virtual void f() = 0; }; // An implementation of a pure virtual function. void Base::f() {…} Used to provide helper functions. void Derived::f() { // Call base class version first. this->Base::f(); … } Note that calling PVF from base is undefined behavior. Base::Base() { f(); // Tries to calls Base::f() during construction. }

214 Can you have a pure virtual destructor? What does it mean? Why?
class Base { public: // Can I do this? virtual ~Base() = 0; }; // Can I do this? Base::~Base() { … } Can have an implementation for a pure virtual destructor. It functions normally, but just means that it must be overridden.

215 Summary: A pure virtual function does NOT mean that you didn’t define it. But you can never instantiate an class that does not have all PVFs defined.

216 Cloning If you have a pointer to a derived type, it is easy to make a copy of it: D *copy_of_d = new D(*d); What if you only have a base class pointer? B *bp = …; // Might point to a D. How to make a copy of it? Need a clone function: B *copy = bp->clone();

217 Accessing Derived From Base
What if you want to have a base class function access derived class information, kind of like this? class Base { public: void func() { printf(“%d\n”, m_color); } }; class D1 : public Base { public: const int m_color; }; class D2 : public Base { public: const int m_color; }; Obviously no way to access a derived class data member. What we really want to access is information, though.

218 Need to pass to base class ctor, or use a virtual function.
class Base { public: Base(int color) : m_color(color) {} void func1() { printf(“%d\n”, m_color); } void func2() { printf(“%d\n”, color()); } virtual int color() const = 0; private: const int m_color; }; class D1 : public Base { public: D1() : Base(RED) {} virtual int color() const { return RED; } }; class D2 : public Base { public: D2() : Base(GREEN) {} virtual int color() const { return GREEN; } }; Which one is more efficient? Which one is less error-prone? If it never changes, then more efficient to just pass to base class ctor.

219 Can get efficiency and robustness this way:
class Base { public: Base() : m_color(color()) {} virtual int color() const = 0; private: const int m_color; }; class D1 : public Base { public: D1() = default; virtual int color() const { return 1; } }; class D2 : public Base { public: D2() = default; virtual int color() const { return 2; } }; Well, not really. The derived type has not been constructed yet, so this is undefined behavior.

220 Could also be a reference:
struct E { virtual ~E() = default; ... }; struct E1 : public E { ... }; struct E2 : public E { ... } struct Base { Base(E &e) : m_e{e} { m_e.some_method(); } // Okay? void func() { m_e.some_method(); } // Okay? E &m_e; }; struct D1 : public Base { D1() : Base{m_e1} {} E1 m_e1; }; struct D2 : public Base { D2() : Base{m_e2} {} E2 m_e2; }; Can’t call in Base ctor before m_e1 or m_e2 is constructed.

221 (Co|Contra|In)variance
Let’s say that we have class Animal and a class Dog. Dog derives from Animal. Is the Dog class a subtype of the Animal class? Does this code compile? Dog d[10]; void foo(Animal *); ... foo(d); Should it? At least two aspects: Is it conceptually safe? (Does it make sense in terms of abstraction?) Does it actually compile? A Dog would be considered a subtype of Animal, but the semantics are not enforced by the language. Yes, it compiles, but should not.

222 Problem? Dog d[10]; void foo(Animal *ap) { ap[2].some_method(); } foo(d); Should an array of Dog objects be a subtype of an array of Animal objects? The code above would have a problem, because the indexing of the Animal * “array” inside foo() would use the size of Animal. The array is laid-out with the size of Dog, however. Note that it compiles, however.

223 Covariance: Contravariance: Invariance:
If B is a subtype of A, then a type composed from B is a subtype of the same composition based on A. Contravariance: If B is a subtype of A, then a type composed from B is a supertype of the same composition based on A. Invariance: If B is a subtype of A, then a type composed composed from B is completely unrelated to the type of the same composition based on A.

224 Should an array of Dog * be a subtype of an array of Animal *?
How about this? Dog *d[10]; void foo(Animal **ap) { ap[2]->some_method(); } foo(d); Should an array of Dog * be a subtype of an array of Animal *? Dog *dp = ...; Animal *ap = dp; // Compiles? Dog **dpp = ...; Animal **app = dpp; // Compiles? Conceptually, this seems okay. (But it’s not, if we can do something like ap[3] = new Cat;.) Won’t compile, however, since a Dog ** cannot be converted to an Animal **.

225 So here’s the implementation of foo().
How about this? void foo(vector<Animal *> &); vector<Dog *> d; ... // Populate d. foo(d); So here’s the implementation of foo(). void foo(vector<Animal *> &v) { v.push_back(new Cat;); } void foo2(const vector<Animal *> &); vector<Dog *> d; ... // Populate d. foo2(d); Conceptually, it seems like it should be a subtype if const, but it doesn’t work in C++, mainly due to limitations in the type system. If not const, obvious problems. If const, it is typesafe.

226 How about this? Animal *get_animal(); Dog *(*get_dog_fp)(); get_dog_fp = &get_animal; Dog *get_dog(); Animal *(*get_animal_fp)(); get_animal_fp = &get_dog; First is not type safe. Second is type safe, but C++ doesn’t support it, due to mechanism limitations.

227 How about this? void accept_animal(Animal *); void (*accept_cat_fp)(Cat *); accept_cat_fp = &accept_animal; void accept_cat(Cat *); void (*accept_animal_fp)(Animal *); accept_animal_fp = &accept_cat; First is type-safe, but C++ doesn’t support it, because it doesn’t know how to convert at run-time. Second is not type-safe.

228 Is this okay? class Base { public: virtual double func(); }; class Derived : public Base { public: virtual int func(); }; No. Can’t have different return types in override.

229 Suppose you have a class hierarchy
Suppose you have a class hierarchy. What should the return type of clone() be? struct B { virtual ??? *clone() const; }; struct D : public B { virtual ??? *clone() const; } struct Animal { virtual Group *getGroup() const; }; struct Bird : public Animal { virtual Flock *getGroup() const; }; struct Flock : public Group { ... }; C++ supports covariance of return types, if a pointer or reference type. This means that a virtual function can return a pointer or reference to type D2 in class D, if class B returns D1 and D2 is derived from D1.

230 What about parameters? struct X { … }; struct X1 : public X { … }; struct X2 : public X { … }; struct A { virtual void foo1(X *); virtual void foo2(X1 *); }; struct A1 : public A { virtual void foo1(X1 *); // Safe to allow? Virtual void foo2(X *); // Safe to allow? }; … X x; X1 x1; X2 x2; A1 a1; A &ar(a1); ar.foo1(&x); // ? ar.foo1(&x1); // ? ar.foo1(&x2); // ? ar.foo2(&x); // ? ar.foo2(&x1); // ? ar.foo2(&x2); // ?

231 C++ does not support contravariance of parameter types in overridden virtual functions.

232 Mutable Arrays/Vectors
Immutable Arrays/Vectors Pointers-to-Members Return Types Parameters Type-safety Invariant Covariant Contravariant C++ support Partial No Yes

233 Missing vtable/typeinfo Errors
Caused by missing definitions of virtual functions. $ cat foo.cpp class A { public: virtual void foo() const {} }; class B : public A { public: virtual void foo() const; }; int main() { B b; } $ g++ foo.cpp /tmp/ccg3JJkc.o: In function `B::B()': foo.cpp:(.text._ZN1BC1Ev[B::B()]+0x1c): undefined reference to `vtable for B‘ collect2: ld returned 1 exit status

234 Efficiency Are virtual functions slow?
If you don’t use a virtual function, you almost always need an if-statement anyway. // Do some prior computation to set-up // the if-test. if (…) { foo->do_version_A(); } else { foo->do_version_B(); } Versus: foo->do_it(); Virtual functions are slower, but not unreasonably slow. Function call overhead is actually often not as big a factor in performance as the fact that it cannot be inlined, which limits optimizations that the compiler can do.

235 [Show cost_of_virt.] Virtual call:
for (int i = 0; i < N; i++) { a[i]->foo(); }

236 Non-virtual (regular) call:
for (int i = 0; i < N; i++) { switch (a[i]->type); case Base_nv::D1: static_cast<D1_nv *>(a[i])->foo(); break; case Base_nv::D2: static_cast<D2_nv *>(a[i])->foo(); break; } } Inlined: for (int i = 0; i < N; i++) { switch (a[i]->type); case Base_nv::D1: static_cast<D1_nv *>(a[i])->inline_foo(); break; case Base_nv::D2: static_cast<D2_nv *>(a[i])->inline_foo(); break; } }

237 Derivation for Implementation
Suppose a car has an engine. You need an engine in your car. Also, engines need a start() method, but also you want a start() method on your car. So you could do this, and automatically get your start method. class Engine { public: void start(); … }; class Car : public Engine { public: // Get the start() method for free. … }; Does this obey the LSP? Probably not. But you can fix by using private inheritance. Or, just don’t do it. Use composition/aggregation. class Car { private: Engine engine; }; Composition implies ownership, while aggregation does not.

238 Private and Protected Inheritance
To derive for implementation, use private or protected inheritance: // All in B are private now. class A : private B { … }; // All public or protected in B is protected now. All private in B are still private. class A : protected B { … }; Can also selectively make more or less restrictive. class B { void priv_func(); public: void foo(); }; class A : public B { private: using B::foo; // Make B::foo private. }; class A2 : private B { public: using B::foo; // Make B::foo public. }; Cannot make something that was private now public in derived, though.

239 Also, something that is not publically inherited will not be automatically converted to base class pointer or reference: class Base { ... }; class Derived_priv : private Base { }; class Derived_pub: public Base { }; void foo(Base &); Derived_priv dpriv; Derived_pub dpub; foo(dpriv); // Syntax error. foo(dpub); // Okay.

240 Interfaces vs. Classes What is the difference between an interface and a class? In common OOP usage: Interface: What the methods are named, the parameters they take, return types, and the semantics. Class: The implementation of the interface, the actual data members, method implementation code, etc.

241 Does Java support polymorphism/virtual-functions?
A Java interface cannot be instantiated, any C++ class with a non-overridden pure virtual function cannot be instantiated. All Java methods are polymorphic. In C++, only virtual functions are polymorphic. In C++, individual functions can be specified as part of the interface, by making them pure virtual. In Java, it is all (interface), or none (class). Does Java have interfaces? Does C++ have interfaces? In C++, there is greater ability to mix and match the features of interfaces and classes, so interface is somewhat implicit. Virtual functions and abstract base classes can be used to make them more explicit. A class with just pure virtual functions and no data members is essentially an interface. Note that Java abstract base class has some relationship.

242 Example: You properly separate interface from class. You create an interface. Java: Use interface keyword. C++: Make all functions pure virtual and make the class have no data members. Create 4 classes that implement the interface. You notice that all 4 classes have the same implementation of a particular function. What do you do? Java: Create a new base class that implements the interface. C++: Move the function into the interface class as non-pure virtual function. In C++, the decision as to whether or not a particular function is part of the interface or implementation (class) is made on a function-by-function basis.

243 Java: interface I { // Everything in here is part of interface. … }; class A implements I { // Everything here is part of the implementation. … }; C++ class Base { public: virtual f1() = 0; // Part of interface. virtual f2() = 0; // Part of interface. void common_func(); // Part of interface AND // implementation. All // classes share the same // implementation, so make it // non-virtual. … };

244 What is the interface of this class?
struct A { void f(int); };

245 Do these two classes have the same interface?
struct D1 { void f(int); }; struct D2 { void f(int); }; Do E1 and E2 have the same interface? struct Interface { virtual void f(int) = 0; ; struct E1 : public Interface { virtual void f(int) { … } }; struct E2 : public Interface { virtual void f(int) { … } }; Using some interpretations of the meaning of interface, D1 and D2 would have the same interface. C++ definitely does not understand this, though.

246 When we use the word ‘interface’, we usually mean to include semantics
When we use the word ‘interface’, we usually mean to include semantics. But the programming language only enforces syntactic. Program to the interface, not to the implementation.

247 Fragile Base Class Often, seemingly safe changes to a base class cause things to break in the derived classes. Example:

248 Someone in your company, but in a different division, writes a string class.
class String { public: virtual void append(char); void length() const { return strlen(buf); } protected: char *buf; }; Your boss says that your app runs too slow. You look into, and discover that the length() function is taking 90% of the time. You don’t control the String class. The author, Joe, is unresponsive because the app for his division runs fine. What do you do? Steal Joe’s office chair and hold it hostage till he fixes String. Tell your boss that you need to buy faster computers. Tell your boss that you need to create your own standalone String class. Derive an optimized version from String.

249 You make it more efficient for length:
class String2 : public String { public: virtual void append(char) { m_length++; this->String::append(c); } void length() const { return m_length; } private: int m_length; }; Good? Obeys LSP? void foo(String *s); String2 s; foo(&s); // Okay? s.length(); It does obey LSP.

250 Joe decides to add a convenience function to allow the user to append a complete string at once.
class String { public: virtual void append(char); void append(const char *str) { for (int i = 0; i < strlen(str); i++) { this->append(str[i]); } } void length() const { return strlen(buf); } private: char *buf; }; Okay? Cannot call append(const char *) on String2 object (without a using declaration in String2), but that won’t break your existing code. So far okay.

251 Does this still obey LSP?
Joe decides that it is much faster to append the complete string at once. class String { public: virtual void append(char); void append(const char *str) { strcat(buf, str); } void length() const { return strlen(buf); } protected: char *buf; }; Does this still obey LSP? This breaks your code.

252 Dynamic Type vs Static Type
Static type: What something is declared as, statically. Dynamic type: What something actually is (i.e., the most derived type). class A { … }; class B1 : public A { … }; class B2 : public A { … }; A *ap = …; // Static type of *ap is A, but the // dynamic type is not known at // compile time. (*ap).foo();

253 In general, C++ is a statically-typed language
In general, C++ is a statically-typed language. Dynamic typing is enabled by polymorphism. struct A { virtual void foo(); }; struct B : public A { virtual void foo(); }; B b; A *ap = &b; (*ap).foo();

254 Run-Time Type Identification (RTTI)
Sometimes you need to know the actual type of an object, when you have just a pointer to the base class. How do you do inheritance in C?

255 Upcasting and downcasting:
Inheritance in C: struct A { int a_member; }; struct B1 { struct A A_sub; int b1_member; }; struct B2 { struct A A_sub; int b2_member; }; Upcasting and downcasting: struct B1 b1; B1_ctor(&b1); A *ap = (A *) &b1; // Upcast B1 *b1p = (B1 *) ap; // Downcast A *ap = …; // Pointer to A obtained somehow. B2 *b2p = (B2 *) ap; // Safe? Casting in C is unsafe.

256 How do we be safe? Try to make sure of type.
Inheritance in C: struct A { int a_member; }; struct B1 { struct A A_sub; int b1_member; }; struct B2 { struct A A_sub; int b2_member; }; How do we be safe? Try to make sure of type. A *ap = …; // Pointer to A obtained somehow. if (/* ap is really a B1 */) { B1 *b1 = (B1 *) ap; // Do something with b1 that is B1 specific. } else if (/* ap is really a B2 */) { B2 *b2 = (B2 *) ap; // Do something with b2 that is B2 specific. }

257 Check ID before downcast.
Use type ID of some kind. struct A { enum { T_B1, T_B2 } id; int a_member; }; struct B1 { struct A A_sub; int b1_member; }; struct B2 { struct A A_sub; int b2_member; }; Check ID before downcast. A *a = …; // Pointer to A obtained somehow. if (a->id == T_B1) { B1 *b1 = (B1 *) a; // Do something with b1 that is B1 specific. } else if (a->id == T_B2) { B2 *b2 = (B2 *) a; // Do something with b2 that is B2 specific. }

258 Error prone: where is the ID set?
struct A { enum { B1_T, B2_T } id; … }; struct B1 { struct A A_sub; }; struct B2 { struct A A_sub; }; B1 b1; // No constructor b1.A_sub.id = B1_T;

259 If using C++, can do it in the constructor.
struct A { const enum { B1_T, B2_T } id; A(enum Id i) : id(i) {} … }; struct B1 { B1() : A_sub(B1_T) {} struct A A_sub; }; struct B2 { B2() : A_sub(B2_T) {} struct A A_sub; }; B1 b1;

260 What are more idiomatic C++ solutions? What does this do?
struct A { virtual ~A() {} }; struct B1 : public A { … }; struct B2 : public A { … }; B1 b; A *a = &b; B2 *bp = dynamic_cast<B2 *>(a); bp == ??; // What is the value of bp? Can use dynamic casting. dynamic_cast downcast only works on polymorphic types. dynamic_cast return 0 if not of the desired derived type. It’s a syntax error if not polymorphic.

261 Can also use RTTI: #include <typeinfo> struct A { virtual ~A() {} }; struct B1 : public A { … }; struct B2 : public A { … }; B1 b; A *a = &b; if (typeid(*a) == typeid(B1)) { … } else if (typeid(*a) == typeid(B2)) { … } else if (typeid(*a) == typeid(B3)) { … } The typeid operator returns a reference to an object of class type_info.

262 The type_info object has a string name that can sometimes be helpful for debugging.
struct A { virtual ~A() {} }; struct B1 : public A { … }; struct B2 : public A { … }; B1 b; A *a = &b; cout << typeid(*a).name() << endl;

263 There is no standard meaning for the string, but usually it’s meaningful and usually it can be decoded. In g++, you can demangle by doing: #include <cxxabi.h> #include <typeinfo> #include <stdlib.h> class A { public: virtual ~A () {} }; class B : public A { }; int main() { B b; A *ap = &b; int ec; const char *demangled_name = abi::__cxa_demangle(typeid(*ap).name(), 0, 0, &ec); printf("%s\n", demangled_name); free((void *) demangled_name); }

264 Is this efficient? Solution?
A *a = …; // Obtained somehow. if (typeid(*a) == typeid(B1)) { … } else if (typeid(*a) == typeid(B2)) { … } else if (typeid(*a) == typeid(B3)) { … } … { } else if (typeid(*a) == typeid(B100)) { … } Solution?

265 You could push the code into the object:
// Original version. if (typeid(*a) == typeid(B1)) { // Some code to do XYZ. } else if (typeid(*a) == typeid(B2)) { // Some code to do ABC. } // Code moved into a virtual function in the // object. virtual void B1::doit() { // Code to do XYZ. } virtual void B2::doit() { // Code to do ABC. } … a->doit(); // Outside of the object. Disadvantage? Intrusive, however.

266 The problem is to efficiently map from the type to the code that should be executed for that type.
struct Code { virtual ~Code() = default; }; struct Code_A { virtual void operator()(A *) const = 0; }; struct Code_A_code1 : public Code_A { virtual void operator()(A *) const { // Do something specific. } }; map<const type_info *, Code *> type_map; Code &code = *type_map.lookup(&typeid(*a)); dynamic_cast<Code_A &>(code)(a); Okay? In order to efficiently store type_info in a map, what operation do we need? The type_info object has before() method. Can’t do the above. No guarantee that same types have same type_info. Static storage duration is guaranteed however.

267 Use the before() method to create a comparator object.
struct Cmp { bool operator()(const type_info *ti1, const type_info *ti2) { return ti1->before(*ti2); }; map<const type_info *, Code *, Cmp> type_map; Code &code = *type_map.lookup(&typeid(*a)); code(a); type_info objects do not have a copy constructor nor an assignment operator. This will fail: map<type_info, SomeType> table;

268 In C++11, can use type_index, which is a convenience wrapper.
#include <typeindex> #include <typeinfo> #include <map> #include <string> #include <iostream> using namespace std; int main() { map<type_index, string> table; table.insert(make_pair(type_index(typeid(int)), "It's an int.")); table.insert(make_pair(type_index(typeid(double)), "It's a double.")); table.insert(make_pair(type_index(typeid(char)), "It's an char.")); cout << table.find(typeid(int))->second << endl; cout << table.find(typeid(double))->second << endl; cout << table.find(typeid(char))->second << endl; }

269 Can also use the hash code, for hash tables (C++11):
size_t type_info::hash_code();

270 Object Slicing The derived part of objects is sliced off when used non-polymorphically. May not be avoidable, be aware! struct Base { ~Base(); virtual void method(); }; struct Derived : public Base { ~Derived(); virtual void method(); }; Derived *dp = new Derived; Base *bp = dp; delete bp; // Calls?

271 What about this? struct Base { virtual void method(); }; struct Derived : public Base { virtual void method(); }; Derived d; void foo(Base b) { b.method(); } foo(d); // Calls which method()?

272 What happens here? Solution?
struct Base { // … int base_var; }; struct Derived : public Base { // … int derived_var; }; int main() { Derived d1(…), d2(…); Base &b1_ref(d1), &b2_ref(d2); b1_ref = b2_ref; } Solution?

273 No great solution. RTTI seems to be the best choice.
struct Base { Base(int b) : base_var(b) {} virtual Base &operator=(const Base &) = 0; int base_var; }; struct Derived : public Base { Derived(int b, int d) : Base(b), derived_var(d) {} virtual Derived &operator=(const Base &); Derived &operator=(const Derived &); int derived_var; }; Derived &Derived::operator=(const Base &o) { return this->operator=(dynamic_cast<Derived &>(o)); } int main() { Derived d1(1, 2), d2(3, 4); Base &b1_ref(d1), &b2_ref(d2); b1_ref = b2_ref; }

274 Lvalues and Rvalues Some values: Do all values have equal status?
1 89 a b + c sin(3.1) Do all values have equal status? Can a appear anywhere that 89 can appear? Assume a has been defined as an int. These slides need to be updated for C++11, but I leave them in here as is for now because the concept is still important even though C++11 has more kinds of lvalues/rvalues.

275 Some values have storage associated with them. You can assign to them.
Others are just values. 2 = a; If you can assign to it, it is called an lvalue, because it can appear on the left-hand side of an assignment. Similarly for rvalue.

276 Rvalue References Consider this String class:
class String { public: String(const char *s = “”) : str(strdup(s)), size(strlen(s)) { } String(const String &s) : str(strdup(s.str)), size(s.size) { } ~String() { free(str); } String &operator=(const String &s) { delete [] str; str = strdup(s.str); size = s.size; } private: const char *str; size_t size; }; String s; s = “hello”;

277 Bind only to temporaries.
Syntax: // Can only bind to temps, and is preferred for temps. void foo(A &&); // Can bind to either, but preference given to rvalue refs. void foo(const A &); void foo(A &); // Can only bind to non-temps. Preference: A a_nonconst; const A a_const; // Is a temp, binds to “A &&” or “const A &”, prefers former. foo(A()); // Binds to “A &” or “const A &”, prefers former. foo(a_nonconst); foo(a_const); // Binds only to “const A &”. Bind only to temporaries. Overload on temporaries. Used to “steal” the resources of temporaries.

278 “Move” Constructors/Assignment
New terminology, “copy assignment”. Copies the right side to the left side. Move constructor: Moves the initializer to the initializee. Move assignment: Moves the right side to the left side. What happens to the “movee” after a move? It’s “empty”. These are mechanisms provided by the language. The semantics/idioms are not enforced by the language.

279 class String { public: String(const char. s = “”) : str(strdup(s
class String { public: String(const char *s = “”) : str(strdup(s.str)), size(strlen(s)) { } String(String &&s) : str(s.str), size(s.size) { s.str = nullptr; s.size = 0; } ~String() { free(str); } String &operator=(const String &s) { delete [] str; str = strdup(s.str); size = s.size; } String &operator=(String &&s) { swap(str, s.str); swap(size, s.size); } private: const char *str; size_t size; }; String s; s = “hello”;

280 Is an Rvalue Reference an Rvalue?
Consider: void foo(A &&a) { … } void foo(A &a) { … } void goo(A &&a) { foo(a); // Calls which overload? cout << a << endl; } The type of an rvalue reference is not an rvalue! The rule is that if it has a name, then it is an lvalue.

281 Move Constructors under a Base Class
Consider a class with a base class: struct Base { Base(Base &&b); // Move ctor. … }; struct Derived : public Base { Derived(Derived &&d) : Base(d) { … } … }; Works? struct Derived : public Base { Derived(Derived &&d) : Base(static_cast<Base &&>(d)) { … } Doesn’t work because d is an lvalue. Need to turn it into a rvalue ref.

282 This is done often enough that there is a std:: function for it:
struct Base { Base(Base &&b); // Move ctor. … }; struct Derived : public Base { Derived(Derived &&d) : Base(std::move(d)) { … } … }; Doesn’t work because d is an lvalue. Need to turn it into a rvalue ref.

283 Operators Consider: template <typename T> class Vector { public: Vector(size_t sz) : (new T[sz]), size(sz) { } Vector &operator=(const Vector &v) { // Copy to data return *this; } private: Vector(T *d, size_t sz) : data(d), size(sz) { } T *const data; const size_t size; }; template <typename T> Vector<T> operator+(const Vector<T> &v1, const Vector<T> &v2) { T *d = new T[v1.sz]; // Fill. return Vector<T>(d, v1.sz); } v1 = v2 + v3 + v4;

284 Write “move” version: template <typename T> Vector<T> && operator+(const Vector<T> &&v1, const Vector<T> &v2) { for (size_t i = 0; i < v1.size; i++) { v1.data[i] += v2.data[i]; } return std::move(v1); } v1 = v2 + v3 + v4;

285 Example: swap [Show rvalue_swap.] Critique this: Using std::move:
template <typename T> void swap(T &o1, T &o2) { T tmp(o1); o1 = o2; o2 = tmp; } Using std::move: #include <utility> template <typename T> void swap(T &o1, T &o2) { T tmp(std::move(o1)); o1 = std::move(o2); o2 = std::move(tmp); }

286 decltype Sometimes you want to get the type of an object or expression, and use it in a declaration/definition. int i; decltype(i) j; int *ip; decltype(ip) ip2; int &r = i; decltype(r) r2(j); // r2 is a reference. The decltype of an lvalue is a reference (except if it is just the bare, original variable). int i1(11); decltype(i1) i2(22); // i2 is an int. decltype((i1)) r1(i2); // r1 is a ref. decltype(i1 + 1) i3(i2); // i3 is ?. decltype(*(&i1 + 1)) i4(i2); // i4 is ?. const int c1(11); decltype(c1) c2(22); // c2 is a const int.

287 The decltype of a function is a function
The decltype of a function is a function. Can use this to simplify function pointer definitions/declarations. int f(double) { … } decltype(f) f2; // int f2(double); decltype(f) *f3(int) { … }

288 auto keyword Pre-C++11, the auto keyword just meant that the variable was of automatic storage duration. This means that it is automatically deallocated when out of scope. In other words, the lifetime of the variable is exactly the same as the scope of the variable. Example: void f() { auto int i; static int j; register int k; extern int l; } Automatic storage duration was the default, however, so the auto keyword was useless. So C++11 introduced an incompatibility with C (and C++98). Previous uses of the auto keyword are now a syntax error. Could they have allowed previous uses if they had wanted to?

289 It now has an additional new meaning, which is that the type of the variable being defined should be deduced from the type of the initializer. auto i = 1; int &r(i); auto i2 = r; // ref or not? auto &i3 = r; auto *i4 = &r; auto i5 = &r; const int i6(1234); auto i7(i6); // Is i7 const or not? auto &i8(i6); i8 = 567; // Okay? auto li{1}; const int *const ip1(&i6); auto ip2(ip1); *ip2 = 567; // Okay? ip2 = &i; // Okay? auto i = 1; int &r(i); auto i2 = r; // i2 is an int. auto &i3 = r; // i3 is a reference. auto *i4 = &r; auto i5 = &r; const int i6(1234); auto i7(i6); // i7 is not const. auto li{1}; // li is not const. const int *ip1(&i6); auto ip2(ip1); *ip2 = 567; // Not okay ip2 = &i; // Okay.

290 The deduced type must be consistent for every variable:
const int i(1234); auto i1(i), &i2(i); // Okay? Very handy for things like iterators: for (map<string, Myclass>::iterator it = map.begin(); …) { … } for (auto it = map.begin(); …) { … }

291 decltype vs auto In some cases, you could use either, though one will be more clear/concise: int n = 1234; decltype(n) i1(n); auto i12{n}; auto discards top-level const and &, however: const int n = 1234; decltype(n) i1(n); // ? auto i2 = n; // ? const int &r{n}; decltype(r) i3{n}; // ? auto i4 = r; //? In C++14, can make auto use the same rules as decltype: const int n = 1234; decltype(n) i1(n); // ? decltype(auto) i2 = n; // ? const int &r{n}; decltype(r) i3{n}; // ? decltype(auto) i4 = r; //? const int n = 1234; decltype(n) i1(n); // i1 is const. auto i2 = n; // i2 is not const. const int &r{n}; decltype(r) i3; // i3 is a ref. auto i4 = r; // i4 is not a ref. auto i{1}; // In flux.

292 Advanced Return Types The return type can trail (C++11):
auto func(int i) -> int (*)[10]; Trailing return types are in class scope for member functions. class A { class B { int f(); … }; B b; … }; auto A::func(int i) -> B { … } auto A::func2() -> decltype(b.f()) { … } In C++14, the return type can be deduced: auto odd_or_even(const int i) { if (i%2 == 1) { return “odd”; } else { return “even”; } } In C++11, the return type of lambdas can also be deduced, but only if just a return statement.

293

294

295 Arrays Must be contiguous in memory. Define one like this:
int a[4] = { 1, 2, 3, 4 }; int a[10]; // Initialized? int a[] = {1, 2}; int a[2] = {1, 2, 3}; // Error int a[2] = {1}; // Remainder are 0 initialized. // Technically an error, but a common extension. int a[0]; Can take the sizeof. What does this print? int a[4]; printf(“%d\n”, sizeof a); printf(“%d\n”, sizeof(int [2000*1000*1000])); Prints: // Should be: printf(“%zu\n”, sizeof(int [2000*1000*1000])); int a[10] is not initialized. sizeof int a[4] is the size of int * 4. Note that sizeof returns a size_t type, which may be bigger than an int.

296 Are arrays passed in and returned by copy or reference?
void foo(int a[10]) { a[9] = 0; } a[9] = 1; foo(a); // What is a[9] now? // What does the return type of foo2() need to // be? ... foo2() { static int a[10]; return a; } You can’t pass arrays, actually what you is a pointer to the first element. So passing arrays is essentially pass-by-reference. Which lines compile? void foo(int a1[10]) { int a2[10]; a2 = 0; // ??? a1 = 0; // ??? } How do you pass in and return arrays by copy then? Wrap in a struct: struct Array { int a[10]; }; Array foo(Array a) { ...; return some_array; } a1 = 0; // Okay, since a1 is actually a pointer. a2 = 0; // Not okay, since a2 is an array.

297 Type of an Array What is the type of an array?
int a[2]; Trick question: The type of an array is a just “an array of the contained type”. It is NOT a pointer. However, it very easily “decays” into a pointer pointing to the first element. int a[4], *ap = a; void foo(int *); foo(a); Note that the & of an array is not the same: int *p = &a; // Syntax error. More examples: typedef int array_t[4]; typedef int *ptr_t; array_t a; // Defines an array. ptr_t ip; // Defines a pointer to an int.

298 Arrays and Pointers Arrays and pointers are closely related. Pointer arithmetic is closely related to array indexing. Assume that we have: int a[10]; int *ip = &a[0]; Then a[2] is equivalent to *(ip + 2) which is equivalent to *(a + 2). Addition and subtraction: int a[10], *ip = a; // What does ip point to? int *ip2 = ip + 3; // What does ip2 point to? int *ip3 = ip2 – 1; // ? int i1, i2; int n = &i1 - &i2; // Okay? int nn = &a[10] - &a[3]; // Okay? int n2 = &a[3] - &a[10]; // Okay? int n3 = &a[3] + &a[1]; // Okay? int a2[3]; int n4 = &a[3] - &a2[0]; // Okay? int a[10], *ip = a; // ip points to first element. int *ip2 = ip + 3; // ip2 points to fourth element. int *ip3 = ip2 – 1; // ip3 points to the third element. int i1, i2; int n = &i1 - &i2; // Not okay, since i1 and i2 are not part of the same array. int nn = &a[10] - &a[3]; // Okay, since part of the same array. There is no a[10] element, but we are allowed to take address of it. int n2 = &a[3] - &a[10]; // Okay. int n3 = &a[3] + &a[1]; // Not okay, makes no sense. int n4 = &a[3] - &a2[0]; // Not okay, different arrays. Which ones are okay? int *ip1 = …, *ip2 = …; ip1 + 2; // Okay. ip1 + ip2; // Not okay. 2 + ip2; // Okay. ip2 – 1; // Okay. 2 – ip1; // Not okay. ip2 – ip1; // Okay, if point into same array. A pointer can validly point to one past the end of the array, and also anywhere inside the array. Cannot validly subtract two pointers that do not point to inside (or one past the end of) the same array. Subtraction of two pointers is okay. Addition of pointer and integer is okay. Pointer – integer is okay. All else is not okay.

299 Which ones are okay? int *ip1 = …, *ip2 = …; ip1 + 2; ip1 + ip2; 2 + ip2; ip2 – 1; 2 – ip1; ip2 – ip1; Subtracting two pointers into an array is the same as subtracting the indices: &a[i] - &a[j] == &(*(a + i)) – &(*(a + j)) == (a + i) – (a + j)) == (a – a) + (i – j) == i - j

300 Pointers and Memory Layout
Consider a variable like this. How is it laid out in memory? int i = 0x , *i_ptr = &i; What is byte-ordering? What kinds can you have? Which is big-endian and which is little-endian? How many different orderings are there? What is the value of i_ptr? 1 2 3 4 100 101 102 103 Address Memory of variable i One possible layout Another possible layout 3 4 1 2 PDP-11 In big-endian, the most-significant byte (the high-order byte) comes first in address order. Can actually have 24 different orderings. The address of anything is always where it begins in memory, regardless of the byte ordering.

301 Another possible layout
What is printed out? int i = 1; char *p = (char *) &i; printf(“%d\n”, (int) *p); 1 Increasing address One possible layout Memory of variable i Another possible layout If big-endian, 0 is printed. If little-endian, 1 is printed.

302 How do you swap byte order? [Editor]
int i = ...; i = ((i >> 24)&0x000000ff) | ((i >> 8)&0x0000ff00) | ((i << 8)&0x00ff0000) | ((i << 24)&0xff000000); int i = ..., j; char *p1 = (char *) &i, *p2 = (char *) &j; p2[0] = p1[3]; p2[1] = p1[2]; p2[2] = p1[1]; p2[3] = p1[0];

303 Complicated Definitions/Declarations
Array of function pointers to functions that take one double parameter and return an int. int (*a[10])(double); A function that take an int parameter and returns a function pointer to a function that takes a double and returns an int. int (*f1(int i))(double){ … } A function that takes an int and returns a pointer to an array of 10 ints. int (*func(int i))[10] { ... } Array of 10 function pointers to functions that take an int parameter and returns a function pointer to a function that takes a double and returns an int. int (*(*a[10])(int))(double);

304 When in doubt, use typedefs.
Array of function pointers to functions that take one double parameter and return an int. int (*a[10])(double); typedef int (*fp_t)(double); fp_t a[10]; A function that take an int parameter and returns a function pointer to a function that takes a double and returns an int. int (*f1(int i))(double){ … } typedef int (*fp_t)(double); fp_t f1(int i) { ... } A function that takes an int parameter and returns a pointer to an array of 10 ints. int (*func(int i))[10] { ... } typedef int (*array_ptr)[10]; array_ptr func(int i) { ... } Array of function pointers to functions that take an int parameter and returns a function pointer to a function that takes a double and returns an int. int (*(*a[10])(int))(double); typedef int (*fp_t)(double); typedef fp_t (*fp2_t)(int); fp2_t a[10];

305 Multidimensional Arrays
Defining multidimensional arrays: int a[2][3] = { 1, 2, 3, 4, 5, 6 }; int a[10][20]; They are really arrays of arrays. For example, int a[2][3] is really an array of size 2 of arrays of size 3. What is the type of the result of the index operation a[0][0]? What is the type of the result of the index operation a[0]? This is two indexing operations. a[1][2] Type of a[0] is an array of size 3.

306 Arrays have rows and columns. Memory, however, is linear
Arrays have rows and columns. Memory, however, is linear. Thus, a 2-D array must be “linearized” to be stored in memory. There are theoretically many ways to do linearization, but only two are common. Which one does C/C++ use? Array a[2][3] a0,0 a0,1 a0,2 a1,0 a1,1 a1,2 a0,0 a0,1 a0,2 a1,0 a1,1 a1,2 Row 0 Row 1 Col 0 Col 1 Col 2 Col 0 Col 1 Col 2 Row major Column major Increasing address C uses row major.

307 Another way to look at it.
int a[2][3]; Row major Column major a0,0 a0,1 a0,2 a1,0 a1,1 a1,2 a0,0 a0,1 a0,2 a1,0 a1,1 a1,2 Col 0 Col 1 Col 2 Row 0 Row 1 a0,0 a1,0 a0,1 a1,1 a0,2 a1,2 a0,0 a0,1 a0,2 a1,0 a1,1 a1,2 Row 0 Row 1 Row 0 Row 1 Row 0 Row 1 Col 1 Col 2 Col 0 Col 1 Col 2 Col 0

308 Puzzle Question Array indexing is equivalent to pointer arithmetic at even a syntactic level, which leads to some strange things: int a[10]; a[2] equivalent to *(a + 2). a[2]  *(a + 2)  *(2 + a)  2[a]. So, we should be able to write: 2[a] = 12345; // Right? Given an array int a[3][3], how many ways can you index the first element, using only the characters 0, [, ], and a? a[0][0] 0[a][0] 0[a[0]] 0[0[a]]

309 Pointers to Arrays Tricky concept, but especially useful as an educational exercise. If you understand them, then you really understand pointers and arrays. ip is a pointer to what? ap is a pointer to what? Where does ip + 1 point? Where does ap + 1 point? int *ip = ...; ? bytes int (*ap)[3] = ...; ? bytes ip 11 ap 11 12 13 ... ip+1 21 31 ip+2 ap+1 21 22 23 31 32 33 ... ap+2 ip is a pointer to an int. ap is a pointer to an array of three ints. sizeof ip == size of a pointer sizeof ip[1] == size of an int sizeof ap == size of a pointer sizeof ap[1] == size of an array of three ints sizeof ap[1][1] == size of an int What is? sizeof ip == ?? sizeof ip[1] == ?? sizeof ap == ?? sizeof ap[1] == ?? sizeof ap[1][1] == ??

310 Can get some complex/weird looking definitions/declarations:
More examples: int (*p1)[10]; int a1[10]; p1 = &a1; // Okay? p1[0] = 1; // Okay? (*p1)[0] = 1; // Okay? p1[0][0] = 1; // Okay? int (*p2)[8]; p2 = &a1; // Okay? Can get some complex/weird looking definitions/declarations: A function that returns a pointer to an array of 10 ints. int (*func())[10]; An array of 10 pointers to functions that take an int and return a pointer to an array of doubles: double (*(*a1[10])(int))[20]; typedef double array_t[20]; typedef array_t *ptr_to_array_t; typedef ptr_to_array_t (*fp_t)(int); fp_t a2[10]; p1 = &a1; // Yes. p1[0] = 1; // No. (*p1)[0] = 1; // Yes. p1[0][0] = 1; // Yes. int (*p2)[8]; p2 = &a1; // No.

311 You can usually just use a pointer instead of a pointer to an array:
int a1[10], a2[10]; int *p; p = a1; p[1] = 1234; p = a2; p[1] = 1234; int a1[10], a2[10]; int (*ap)[10]; ap = &a1; (*ap)[1] = 1234; ap = &a2; (*ap)[1] = 1234; However, in the case of multidimensional, dynamic arrays, pointers to arrays can do things that simple pointers cannot.

312 Can also have a reference to an array.
int a[10], (&alias)[10](a); Mainly of use later on in templates.

313 Multidimensional Array Type Decay
An array containing type T decays to a pointer to type T. void f1(int *); int a[10]; f1(a); // Okay? void f2(MyClass *); MyClass a[10]; f2(a); // Okay? void f3(int **); int a[10][20]; f3(a); // Okay? Answers on next slide.

314 An array of ints decays to a pointer to an int.
int a[10]; // a decays to int *. void f1(int *); f1(a); // Works. An array of structs decays to a pointer to the struct. MyClass a[10]; // a decays to MyClass *. void f2(MyClass *); f2(a); // Works. A multidimensional array is an array of arrays. So, a multidimensional array decays to a pointer to an array, not a pointer to a pointer. int a[10][20]; // a decays to int (*)[20]. void f3(int (*ap)[20]); void f4(int **); f3(a); // Works. f4(a); // Syntax error.

315 Dynamic Arrays ... ... ... ... The “array of pointers” way. 2-D
Does each row need to have the same number of columns? No. Number of columns 2-D ... 111 112 113 ... 121 122 123 Number of rows ... 131 132 133 ...

316 Number of planes 3-D ... 311 312 313 ... ... 321 322 323 ... 331 332 333 ... Plane 2 Number of columns ... ... 211 212 213 111 112 113 ... ... 221 222 223 121 122 123 Number of rows Each plane can have a different number of rows, and each row can have a different number of columns. This gives great flexibility in the shape of the array. ... ... 231 232 233 131 132 133 ... ... Plane 1 Plane 0

317 The code for the “array of pointers” way.
void f_3ptr(int ***); int ***a = (int ***) malloc(np*sizeof(int **)); for (size_t i = 0; i < np; i++) { a[i] = (int **) malloc(nr*sizeof(int *)); for (size_t j = 0; j < nr; j++) { a[i][j] = (int *) malloc(nc*sizeof(int)); } } f_3ptr(a); for (size_t i = 0; i < np; i++) { for (size_t j = 0; j < nr; j++) { free(a[i][j]); } free(a[i]); } free(a);

318 If only one dimension needs to be dynamic, you can use a pointer to an array.
void f(int (*const a)[2][3]); int (*a)[2][3] = (int (*)[2][3]) malloc(np*sizeof(int [2][3])); f(a); free(a); Number of columns must be known at compile-time a Col 0 Col 1 Col 2 Row 0 111 112 113 Plane 0 Row 1 121 122 123 Number of rows must be known at compile-time Row 0 211 212 213 Plane 1 Row 1 221 222 223 Row 0 311 312 313 Plane 2 Row 1 321 322 323 ... np planes

319 2D usage: A dynamically-determined number of rows, but a fixed number of columns.
// Pointer to an array of 10 columns. int (*a)[10]; // Use it as an array with n_rows and 10 columns. a = (int (*)[10]) malloc(sizeof(int [10])*n_rows); a[0] refers to the first array of 10. a[1] refers to the second array of 10. a + 1 points to the second array of 10. a + 2 points to the third array of 10. a[0][2] refers to the third column in the first array. a[1][3] refers to the fourth column in the second array. (*(a + 2))[5] refers to the sixth column in the third array.

320 In C99, can use variable length arrays.
The “array of pointers” way is slower, but more flexible.

321 Stop here Spring 2018

322 Types

323 Declarations Declarations associate a name with a type.
A declaration does not actually allocate storage, while a definition does. For any given variable or function, there must be only a single definition, but there may be multiple declarations. (One Definition Rule)

324 Names Maximum length is implementation-defined.
Do not use leading underscores or double underscores. Do not use _DEBUG. Why not? Rule of Thumb: Short names in short scopes. Long names in long scopes. Names with leading underscores are reserved for the compiler and standard library implementers. If a name is going to “live” only a few lines, that’s a short scope, so the name can be short. If it is a global name, on the other hand, then the scope is very long, so it should have a long name.

325 Booleans bool Two values: true or false
What is relation of truthiness to numerical and pointer values? // What values do they get? int i = true, j = false; if (10) { ... } else { ... } if (-10) { ... } else { ... } if (0) { ... } else { ... } if (0.0) { ... } else { ... } int *a = ...; if (a) { ... } else { ... } if ("") { ... } else { ... } true has the value 1, false value 0. Non-zero is true, 0 is false. Floating point 0.0 is also false. Non-null pointer is true, otherwise false. The type of the string literal (“”) is an array, but it decays to a pointer to the beginning of the literal. Since it is non-null, it is true.

326 Character Types char holds a character of the implementation’s character set. sizeof(char) is defined to be 1. What if a character takes 12 bits? Does sizeof a char return 1.5? May be signed or unsigned, depending on implementation. char c = 0; c = c – 1; // Output below depends on signedness of char. printf("%d\n", int(c)); If you need something specific, can specify it. signed char c = 0; c = c – 1; printf(“%d\n”, (int) c); // Always -1. unsigned char c = 0; c = c – 1; cout << int(c) << endl; // Always 255. 'a' is a character literal. 'abcd' is a multicharacter literal. Sizeof always returns the size of a single character, regardless of how many bits it actually takes.

327 Integer Types Integer types: Literals: What does this print?
int, short, long, long long (usually 64-bit) signed, unsigned Literals: 0xff, 123, 073, 123U, 1234L, 1234LL Note that there is no way to specify a literal short, but you can cast: short(1). What does this print? #include <stdio.h> int main() { int i = ; printf("%d\n", (4000*i)/4000); } Prints [Show overflow/] Technically, overflow of signed integers is undefined. Commonly assumed to be benign, however. If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined, unless such an expression is a constant expression (5.19), in which case the program is ill formed. [Note: most existing implementations of C++ ignore integer overflows. Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is usually adjustable by a library function. ] (five followed by 9 zeroes) is too big, so needs a LL, or L, depending on the architecture. Use L and U and UL to make long constants, etc. On Linux with g++, prints: and Could be arch dependent, not just OS and compiler dependent.

328 Floating-Point Types Floating point types:
float, double (usually 8 bytes), long double (usually more than 8 bytes) 3.14 (defaults to double), 1.23f, 3.4L float pi_f = 3.14f; double pi_d = 3.14; long double pi_l = 3.14L; printf(“%f, %f\n”, pi_f, pi_d); printf("%lf\n", pi_f); printf("%lf\n", pi_l); // Okay? printf("%Lf\n", pi_l); // Okay? Alignment is to 8, usually, for doubles. How are floating point numbers actually represented, in terms of actual bits? A float gets promoted to a double when passed as an argument where there is no parameter to give it a type. %lf is same as %f. %Lf is a long double. So: printf("%lf\n", pi_l); // Not okay. printf("%Lf\n", pi_l); // Okay. FP numbers are represented as a mantissa and exponent, in binary.

329 Type Aliases typedef: typedef char *foo; typedef long long int64; Syntax rule to remember is that the name of the type goes where the name of the variable would normally go. Alias declaration (additional functionality with templates) (C++11): using foo = char *; using int64 = long long;

330 Can be used to define types for arrays:
typedef int array_t[5]; using array2_t = int [5]; The length is part of the type. Can also be used define function types and function pointer types. typedef int f(double); using fp = int (*)(double); int func(double); f foo; // What does this do? foo = func; // Okay? fp foo2; // What does this do? foo2 = func; // Okay? f foo; // Declares foo as a function. fp foo2; // Declares foo2 as a function pointer.

331 Literal Constants If you write:
printf(“%d\n”, 15); Where are such constants stored in the executable? Some constants are immediate operands (stored in the instruction). Some are stored in data segment. No time this semester (2014-1s).

332 Structures Structure defined with: Fields are accessed with:
struct foo { int i; double x; }; Fields are accessed with: struct foo f, *fp = &f; fp->x and f.x; Initialized like this (same as arrays): struct foo f = {1, 3.14}; Can be returned, assigned, etc. struct foo g; g = f; struct foo f() { struct foo f; ...; return f; } struct foo obj; obj = f(); // Dangerous? Okay, since it is returned by value.

333 There is also padding at the end of a struct. Type equivalence:
Structures can have padding between fields, so the size of a structure is typically more than the sum of the size of each field. Purpose of the padding is to maintain alignment. Typically, a variable must start at an address that is a multiple of the size. There is also padding at the end of a struct. For arrays, discussed later. Type equivalence: Does this compile? struct T1 { int i; }; struct T2 {int i;}; T1 *p1 = ...; T2 *p2 = p1; Does not compile, since they are different types even with same members. Let’s suppose you want to put an array in a struct, but the size might be different. What do you do? Flexible array member. struct foo { int i; double a[]; }; struct foo *fp = malloc(sizeof(struct foo) + n*sizeof(double)); fp->a[n – 1] = 3.14;

334 Can you make this compile?
struct A { int i; B b_field; }; struct B { double x; A a_field; }; Similar case: struct A { int i; struct A a; // Need the struct? }; Can get same effect: struct A { int i; A *inner; }; Structures can refer to each other recursively using pointers, but not contain each other recursively. One link of the recursion must be by pointer. Recursive classes, it’s an error. Size is infinite.

335 Enumerations Holds a set of values specified by the user.
enum state { LETTER = 1, SPACE }; Why might you want to begin with 1? The values can be used as constants: foo = SPACE; int a[SPACE]; // Array of length 2. Can reset. enum Color { RED = 1, BLUE, GREEN = 1 }; Beginning with 1 is a small bit of defensive programming. It increases the chance that if you ever forget to initialize an enum variable, it will be zero, which is not a valid enum value.

336 C++11 adds scoped enumerations (enum classes):
The scope of an unscoped enumerator is the same as enumeration itself, and also in the “enum” scope. enum PotionType { HEALING, SPEED, POISON }; enum StatType { DEXTERITY, HEALTH }; int HEALTH = 10; // Error. StatType st = HEALTH; StatType st = StatType::DEXTERITY st2 = DEXTERITY; enum WandType { SPEED, FIREBALL }; // Error. C++11 adds scoped enumerations (enum classes): enum class PotionType { HEALTH, SPEED, POISON }; enum struct StatType { DEXTERITY, HEALTH }; int HEALTH = 10; StatType st = StatType::HEALTH; PotionType pt = PotionType::HEALTH; enum PotionType { HEALTH, SPEED, POISON }; // Error. enum StatType { DEXTERITY, HEALTH }; // Error. int HEALTH = 10; // Error.

337 Enumerations can be used in overloading:
enum Color { R, G, B }; void foo(Color); void foo(int); Color c; foo(c); foo(1); Unscoped enums will implicitly convert to an integral type, but not scoped. void goo(int); goo(c); // Okay, implicit conversion. enum struct Vehicle { CAR, TRUCK } v(CAR); goo(v); // Error.

338 C++11 allows you to specify the size of an enum:
enum Color : char { R, G, B }; enum Part : char { Widget = }; // ? If you don’t specify it: For unscoped enums, the type is unknown. For scoped enums, it is int. You can also forward declare enums in C++11, but you must specify the size: enum Color; // Error. enum Color : char; // Okay. // Okay, defaults to int. enum class AccountType; As expected, if the value is too large, that’s an error.

339 References A reference is an alternate name, or alias. Arguably syntactic sugar. Implemented with pointers: At the implementation level, a pointer that doesn’t look like one. Defines another name for a variable. int i, &alias = i, &alias2(i); i = 2; alias = 3; // Accessing the same variable. const int &i = 1; // Okay? int &j = 1; // Okay? Initializer of non-constant reference must be lvalue. Initializer of constant reference may be expression. Functions that return references can be used on the left-hand side of an assignment. double &at(int i) { static double a[10]; return a[i]; } at(1) = ; const inst &i = 1; // Okay. int &j = 1; // Not okay. Maybe add something about lifetime extension of temp that has a ref bound to it.

340 Cannot be re-bound, unlike a pointer.
int &i = j; i = &k; // Error. &i = k; // Error.

341 Pointers Exampless: Is a pointer just an address?
int *i_ptr, **i_pptr, ****i_pppptr; A *a_ptr; char *buf; Is a pointer just an address? A type that points to an lvalue of another type. Essentially a typed address. Address-of operator (&): What can you take the address of? Smart-alecky answer: Anything that has an address (i.e., any lvalue, more or less). int var; &var, &(*ptr), &(ptr + 2), &(*(ptr + 2)), &(*(int *) 0xfff01234) Address of reference: int var, &ref(var), &ref2 = var; &var is the same as &ref. int i; int **ip = &(&i); int i, *ip = &i, **ipp = &ip; int *ip = &(1 + 2); Dereference operator (*): What can you dereference? Anything that is of pointer type. &(ptr + 2) is an error. &(&i) is an error, since &i is not an lvalue, and so does not have an address. &(1+2) is also an error, since it might be an immediate operand, and not have an address.

342 Example of using pointers to pointers:
Suppose you have a singly linked list [interactive]: struct Node { int value; Node *next; }; Node *head = 0; One way to append to it: Node *end = 0; void append(Node *n) { n->next = 0; if (end == 0) { head = end = n; } else { end->next = n; end = n; } } Another way: Node **end = &head; void append(Node *n) { n->next = 0; *end = n; end = &n->next; }

343 Function Pointers Suppose you have a sort function, kind of like:
void sort(int *array) { … } Can you write code that you can use for both ints, doubles, or your own struct type? [Editor] // Sort in ascending order. void sort(void *array, int n, int sz, bool (*less)(void *, void*)) { // Compare i-th and j-th element. char *a = (char *) array; if ((*less)(a + i*sz, a + j*sz)) { } bool Person_less(void *a, void *b) { Person *p1 = (Person *) a; Person *p2 = (Person *) b; return strcmp(p1->name, p2->name) < 0; } Person *table = malloc(n_people*sizeof(Person)); sort(table, n_people, sizeof(Person), Person_less);

344 Suppose you now want to be able to sort in increasing order or decreasing order. What do you do?
Change the comparison function. // Sort in ascending order. void sort(void *array, int sz, bool (*less)(void *, void*)) { // Compare i-th and j-th element. char *a = (char *) array; if ((*less)(a + i*sz, a + j*sz)) { } bool Person_greater(void *a, void *b) { Person *p1 = (Person *) a; Person *p2 = (Person *) b; return strcmp(p1->name, p2->name) > 0; } Person *table = malloc(n_people*sizeof(Person)); // Sort in descending order. sort(table, sizeof(Person), Person_greater); // Sort in ascending order. sort(table, sizeof(Person), Person_less);

345 Can be reassigned, of course:
int foo1(double x) { ... } int foo2(double x) { ... } void foo3(double x) { ... } int (*fp)(double); fp = &foo1; (*fp)(3.14); // Calls foo1(). fp = &foo2; (*fp)(3.14); // Calls foo2(). Types must match: fp = &foo3; // Syntax error.

346 Null Pointers What is the value of i?
char *p = 0; int i = int(p); What is the value of a null pointer? It's actually unspecified. But wait, is it not 0? Not really. 0 is a special code to the compiler. When it is used in a pointer context, the compiler automatically converts it to whatever the null pointer value is. Consider: char *p = 0; int i = int(p); // Is i 0? int zero = 0; char *p = (char *) zero; // Is p null? char *p = 0; int *i = 0; (unsigned long long) p ?= (unsigned long long) i In C++11, a new keyword was introduced, nullptr. It’s best to use this. The variable i is not guaranteed to be 0. The pointer p is not guaranteed to be null. A null char pointer is not guaranteed to be the same bit pattern as a null int pointer.

347 What about NULL? It’s a macro.
Let’s assume it is defined as (void *) 0. Does this work? char *p = NULL; Let’s assume it is defined as 0. Does this work? On Linux, it is defined as follows: #ifndef _LINUX_STDDEF_H #define _LINUX_STDDEF_H #undef NULL #if defined(__cplusplus) #define NULL 0 #else #define NULL ((void *)0) #endif #endif

348 Consider the execlp() system call:
int execl(const char *, const char *arg0, ...); execl(“./a.out”, “a.out”, “arg1”, “arg2”, 0); Is this correct usage? Not correct usage because the compiler has no way of knowing that the last argument is actually a null pointer. So need to cast. execl(“./a.out”, “a.out”, “arg1”, “arg2”, (const char *) 0);

349 C Strings A string in C is just an array of characters that is terminated by a 0 element. Note that you can have an array of characters that is not 0 terminated, and thus not really a string. What is the difference between? char a1[] = “abcd”; const char *a2 = “abcd”; char a3[4] = {'a', 'b', 'c', 'd'}; sizeof a1 == ? sizeof a2 == ? sizeof a3 == ? a2[0] = 'z'; // Okay? a1 = "a"; // Okay? a2 = "b"; // Okay? a3 = "c"; // Okay? 5 Size of a pointer 4 No, can’t write to literal. Can’t assign to array. Yes. char a[] = "abcd" creates an array initialized to the contents of the string, including the null char.

350 String Literals “hello“ // A string literal
What is the type of “hello”? Type is “array of the appropriate number of const characters”: const char[6] sizeof("hello") == 6 What is the type in C? Is this okay code? void error(char *s) { printf(“error: %s\n”, s); } … error(“Bad syntax”); const char *foo() {return "hello";} Is this an error? const char *str = "This is a long " "string."; In C, the type of a string literal was an array of chars, not an array of const chars. The code is not good because if you call it with string, it will give at least a warning, because you are converting a string literal to a char *. It should be a const char *. The second one is okay, because the storage duration of a string literal is static. It lasts through the lifetime of the program. There is no syntax error. C/C++ will automatically concatenate strings that are adjacent. It happens at compile-time, so there is no performance penalty.

351 Void What is it used for? Does this compile? What about arithmetic?
Function returns. Pointers to unknown types. Does this compile? void *vp = …; char *cp = vp; What about arithmetic? void *vp = …; void *vp2 = vp + 1; // Okay? Compiles in C, but not C++. C++ requires a cast. Some compilers will accept void as size 1, but it’s bad code.

352 In C, does this compile? What about C++?
int f1() { return 1; } int main() { f1(1); } int f2(); int main() { f2(1); } int f3(void); int main() { f3(1); } In C, void in function definition is redundant, but not in a declaration. In C++, void in either a function definition or declaration is redundant. int f4(void) { return 1; };

353 Constness const int foo = 30; int const foo = 30; // Same thing.
Cannot assign to foo. foo = 10; // No. const int *foo; Cannot assign to *foo, but can assign to foo itself. *foo = 1; // No. foo = &some_int_var; // Okay. int *const foo = &some_int; Cannot assign to foo (which is a pointer), but can assign to what is pointed to. foo = &some_other_int_var; // No. *foo = 1234; // Okay. const int *const foo = &some_int; Cannot assign to anything.

354 int ** const *** const * const *p;
p = ...; // Okay? *p = ...; // Okay? **p = ...; // Okay? ***p = ...; // Okay? ****p = ...; // Okay? *****p = ...; // Okay? ******p = ...; // Okay? *******p = ...; // Okay? p = ...; // Yes. *p = ...; // No. **p = ...; // No. ***p = ...; // Yes. ****p = ...; // Yes. *****p = ...; // No. ******p = ...; // Yes. *******p = ...; // Yes.

355 constexpr In C++03, there is no way to cleanly say that something is a compile time constant. const int i1 = 1; const int i2 = 3*4; const int i3 = MyClass<2, 4>::RED; const int i4 = product_if_odd(3, 4, 5); Furthermore, you can never call a function to compute a compile time constant. C++11 has constexpr to fix this: constexpr int i1 = 1; // Body must be just single return statement. constexpr int product_if_odd(int i, int j, int k) { return i%2 == 1 ? i*j*k : 0; } // Syntax error if foo() is not constexpr. constexpr int i2 = foo(3); constexpr int i3 = product_if_odd(3, 4, 5); // Okay.

356 C++14 greatly relaxes the restrictions on constexpr:
constexpr int count_a(const char *s) { int c = 0; for (; *s != '\0'; s++) { if (*s == 'a') { c++; } s++; } return c; }

357 Another C++14 example: constexpr bool is_prime(int n) { for (auto i = 2; i*i <= n; i++) { if (n%i == 0) { return false; } } return true; }

358 Can also have constexpr arrays and objects, as long as the objects are of literal type.
struct A { int i, j; }; constexpr A foo() { return {1,2}; } constexpr A a(foo()); constexpr A aa[2] = { {1, 2}, {3, 4} };

359 Volatile [Show flag.] int *flags = some_special_hardware_address; while (true) { … if (*flags & 0x1) { … } … } Does above work? Does *flags get reloaded every iteration? volatile int *flags = some_special_hardware_address; while (true) { … if (*flags & 0x1) { … } … } The contents of what ‘flags’ is pointing to will not get reloaded everytime, unless you use a ‘volatile’ qualifier. This qualifier tells the compiler that the contents may change at any time.

360 Type Conversions Implicit conversions
They are “automatic”, in the sense that you don’t need to add anything to make them happen. What are some? int to double Pointer to derived to pointer to base (upcast) Two situations where conversions come into play. Converting to a given type, such as assignment or argument passing. Two operands of an arithmetic operator. Arithmetic conversions: signed/unsigned combinations are tricky: If both operands are same size, the signed is converted to the unsigned. Otherwise, it’s more or less the smaller to the larger.

361 int vs. unsigned long cout << UL << endl; cout << (-1 < 1UL) << endl; cout << UL << endl; cout << (-2 < 1UL) << endl; cout << UL << endl; cout << (-1 < 2UL) << endl; cout << UL << endl; cout << (-2 < 2UL) << endl; On 32-bit Linux g++: -1 + 1UL == 0 -1 < 1UL == false -2 + 1UL == -2 < 1UL == false -1 + 2UL == 1 -1 < 2UL == false -2 + 2UL == 0 -2 < 2UL == false

362 int vs. unsigned long cout << UL << endl; cout << (-1 < 1UL) << endl; cout << UL << endl; cout << (-2 < 1UL) << endl; cout << UL << endl; cout << (-1 < 2UL) << endl; cout << UL << endl; cout << (-2 < 2UL) << endl; On 64-bit Linux g++: -1 + 1UL == 0 -1 < 1UL == false -2 + 1UL == -2 < 1UL == false -1 + 2UL == 1 -1 < 2UL == false -2 + 2UL == 0 -2 < 2UL == false

363 long int vs. unsigned int
cout << -1L + 1U << endl; // L1 cout << -1L + 1U LL << endl; // L2 cout << -1L + 1U + 2LL - 1 << endl; // L3 cout << -2L + 1U << endl; // L4 cout << (-1L < 1U) << endl; // L5 On 64-bit: -1L + 1U == 0 -1L + 1U – 1 + 2LL == 1 -1L + 1U + 2LL – 1 == 1 -2L + 1U == -1 -1L < 1U == true On 32-bit: -1L + 1U = 0 -1L + 1U – 1 + 2LL == -2L + 1U == -1L < 1U == false

364 What does this print? size_t s3 = 3, s5 = 5; size_t s10 = 10; long long ll10 = 10; s10 += s3 - s5; // L1 ll10 += s3 - s5; // L2 cout << s10 << “, “ << ll10 << endl; size_t is 64-bit unsigned on 64-bit platforms, and 32-bit unsigned on 32-bit platforms. long long is a 64-bit signed integer on both platforms. On 64-bit: 8, 8 On 32-bit: 8,

365 What does this print? cout << *2000*1L << endl; cout << 1L* *2000 << endl; Intel 32-bit with g++: Intel 64-bit with g++:

366 bool conversions Does this print “Yes” or “No”? What about this?
int i = 10; if (i) { cout << “Yes” << endl; } else { cout << “No” << endl; } What about this? int i = 10; if (i == true) { cout << “Yes” << endl; } else { cout << “No” << endl; } In the second case, the true gets converted to 1.

367 Numerical Conversions
3.9.1/4 Unsigned integers, declared unsigned, shall obey the laws of arithmetic modulo 2n where n is the number of bits in the value representation of that particular size of integer. 4.5 Integral promotions [conv.prom] An rvalue of type char, signed char, unsigned char, short int, or unsigned short int can be converted to an rvalue of type int if int can represent all the values of the source type; otherwise, the source rvalue can be converted to an rvalue of type unsigned int. An rvalue of type wchar_t (3.9.1) or an enumeration type (7.2) can be converted to an rvalue of the first of the following types that can represent all the values of its underlying type: int, unsigned int, long, or unsigned long. An rvalue for an integral bitfield (9.6) can be converted to an rvalue of type int if int can represent all the values of the bitfield; otherwise, it can be converted to unsigned int if unsigned int can represent all the values of the bitfield. If the bitfield is larger yet, no integral promotion applies to it. If the bitfield has an enumerated type, it is treated as any other value of that type for promotion purposes. An rvalue of type bool can be converted to an rvalue of type int, with false becoming zero and true becoming one. These conversions are called integral promotions. 4.6 Floating point promotion [conv.fpprom] An rvalue of type float can be converted to an rvalue of type double. The value is unchanged. This conversion is called floating point promotion. 4.7 Integral conversions [conv.integral] An rvalue of an integer type can be converted to an rvalue of another integer type. An rvalue of an enumeration type can be converted to an rvalue of an integer type. If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). ] If the destination type is signed, the value is unchanged if it can be represented in the destination type (and bitfield width); otherwise, the value is implementation defined. If the destination type is bool, see If the source type is bool, the value false is converted to zero and the value true is converted to one. The conversions allowed as integral promotions are excluded from the set of integral conversions. 4.8 Floating point conversions [conv.double] An rvalue of floating point type can be converted to an rvalue of another floating point type. If the source value can be exactly represented in the destination type, the result of the conversion is that exact representation. If the source value is between two adjacent destination values, the result of the conversion is an implementation defined choice of either of those values. Otherwise, the behavior is undefined. The conversions allowed as floating point promotions are excluded from the set of floating point conversions. 4.9 Floating-integral conversions [conv.fpint] An rvrvalue of an integer type. The conversion truncates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot be represented in the destination type. [Note: If the destination type is bool, see ] An rvalue of an integer type or of an enumeration type can be converted to an rvalue of a floating point type. The result is exact if possible. Otherwise, it is an implementation defined choice of either the next lower or higher representable value. [Note: loss of precision alue of a floating point type can be converted to an occurs if the integral value cannot be represented exactly as a value of the floating type. ] If the source type is bool, the value false is converted to zero and the value true is converted to one. 5/9 Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows: If either operand is of type long double, the other shall be converted to long double. Otherwise, if either operand is double, the other shall be converted to double. Otherwise, if either operand is float, the other shall be converted to float. Otherwise, the integral promotions (4.5) shall be performed on both operands. Then, if either operand is unsigned long the other shall be converted to unsigned long. Otherwise, if one operand is a long int and the other unsigned int, then if a long int can represent all the values of an unsigned int, the unsigned int shall be converted to a long int; otherwise both operands shall be converted to unsigned long int. Otherwise, if either operand is long, the other shall be converted to long. Otherwise, if either operand is unsigned, the other shall be converted to unsigned. [Note: otherwise, the only remaining case is that both operands are int ]

368 Pointers to Members Say that you have a class with two similar methods, op1 and op2. class A { … void op1(); // The two are similar but not void op2(); // the same. }; Now you want to write a function to repeat any operation N times. How would you do this? All problems in computer science can be solved with another level of indirection – Butler Lampson Could pass in a flag to a repeat function that would select which op, though not very elegant.

369 This suggests function pointers. How is this?
void repeat(int n_reps, void (*fp)()) { for (int i = 0; i < n_reps; i++) { (*fp)(); } } … repeat(10, &op1); // Do op1 ten times. repeat(11, &op2); // Do op2 eleven times. How about? repeat(10, &A::op1); repeat(11, &A::op2); Where’s the object? How about this below? void repeat(int n_reps, A *a, void (*fp)()) { for (int i = 0; i < n_reps; i++) { a->(*fp)(); } } … repeat(10, &a, &A::op1); More reasonable, but syntax is wrong, because a function pointer is very different from a pointer to a member.

370 Can you do this? struct A { void f1() { … } }; void (*func_ptr)(); func_ptr = &A::f1; // Okay? No, a pointer to member has a different type.

371 Pointers to member functions. Examples:
struct A { int f1(int i) { … } int f2(int i) { … } }; int (A::*fp)(int); A a; fp = &A::f1; (a.*fp)(1); // Calls A::f1(1). fp = &A::f2; (a.*fp)(1); // Calls A::f2(1). void foo(A *ap, int (A::*fp)(int)); foo(&a, &A::f1);

372 #include <stdio.h> class A { public: int f(int) { printf("Called A::f().\n"); } int g(int) { printf("Called A::g().\n"); } }; void func(A *ap, int (A::*ptm)(int)) { (ap->*ptm)(0); } int main() { A a; func(&a, &A::f); func(&a, &A::g); }

373 Can also have pointers to data members.
Pointer to static member function: Not associated with object, so same as regular function pointer. struct A { static void f(); }; void (*fp)() = &A::f; Can also have pointers to data members. struct A { int i, j; }; A a; int A::*ip; ip = &A::i; a.*ip = 1234; ip = &A::j; a.*ip = 5678;

374 A pointer-to-member of a base class can be converted to one of the derived class, but not vice versa: struct B { void foo1(); }; struct D : public B { void foo2(); }; void (B::*ptm1)() = &D::foo2; // Error. void (D::*ptm2)() = &B::foo1; // Okay.

375 What about? struct B { private: void foo1(); }; struct D : public B { void foo2(); }; void (D::*ptm2)() = &B::foo1; // Okay?

376 Can be a confusing when combined with static members:
struct A { void (*fp)(int); static void (*static_fp)(int); static void (*A::*static_ptm)(int); }; void (*A::*A::static_ptm)(int) = &A::fp; void (*A::*A::static_ptm)(int)(&A::fp);

377 PtM Example Suppose you were writing the code for the computer-controlled player in a game. class Player { public: void fire_long_bow(); void fire_crossbow(); }; if (complicated_decision_to_use_longbow) { while (complicated_decision_to_keep_fighting) { npc->fire_long_bow(); } } else { while (complicated_decision_to_keep_fighting) { npc->fire_crossbow(); } }

378 Could make the decision once, then use a flag:
bool use_long_bow = false; if (complicated_decision_to_use_longbow) { use_long_bow = true; } while (complicated_decision_to_keep_fighting) { if (use_long_bow) { npc->fire_long_bow(); } else { npc->fire_crossbow(); } }

379 Gets messy with lots of weapons.
while (complicated_decision_to_keep_fighting) { switch (weapon_type_decided_in_code_above) { case LONG_BOW: npc->fire_long_bow(); break; case CROSSBOW: npc->fire_crossbow(); break; case SLING: npc->fire_sling(); break; default: npc->uh_oh_run_away(); }

380 Use pointer-to-member:
void (Character::*fire)(); if (complicated_decision_to_use_longbow) { fire = &Character::fire_long_bow; } else { fire = &Character::fire_crossbow; } while (complicated_decision_to_keep_fighting) { (npc->*fire)(); }

381 Object Identity Let’s say that you have two references to two objects belonging to some struct (or class). How can you tell if they refer to the same object? struct Foo { int i; double x; }; void some_func(const Foo &r1, const Foo &r2) { // Are r1 and r2 actually the same object? if (&r1 == &r2) { ... } } Two objects are the same object if and only if they have the same address. In theory, some architectures could have a segmented address, which results in some overlap between segments. In such a situation, two different bit patterns could point to the same address. However, due to this requirement in the C++ standard, on such architectures the compiler must make sure that the generated code does not encounter this situation.

382 sizeof empty struct [empty_struct/] What does this print in C?
struct Empty {}; int main() {    struct Empty a[10];    printf("distance: %d\n", (int) (&a[1] - &a[0])); } This causes a numerical exception.

383 What is should sizeof return for this? If it returned 0, then…
struct Empty {}; If it returned 0, then… Empty a[10]; Empty *a1 = &a[1]; Empty *a2 = &a[2]; if (a1 == a2) { printf(“Same object!\n”); } &a[i] - &a[j] ?== (i – j) Therefore, must return more than zero. Typically the size of empty struct is 1.

384 Operators and Expressions

385 Operators Arithmetic: + - / * %
-10/3 == ? -10%3 == ? Must always be true: (a/b)*b + a%b == a. Relational: < <= > >= == != && || Is this code safe? int *ip = …; if (ip != 0 && *ip == 1) { … } Negation: ! Increment and decrement: -- ++ Bitwise: & | ^ ~ << >> Assignment: = =+ =/ … Value of an assignment operator is value of the result. int a; printf(“%d\n”, a = 1); // Prints 1 printf(“%d\n”, 3*(a += 2)); // Prints 9. Also an lvalue (but not in C): (a = 1) = 3; Value of mod with negative numbers is implementation defined in C89. In C99 and new C++, integer division always truncates, so mod is now well-defined. Logical operators && and || will short-circuit.

386 Conditional expression: Is this a syntax error?
i = a > b ? 1 : 2 Is this a syntax error? i = f(1), g(2); sizeof, &var, * (dereference) Comma operator evaluates each expression in sequence. Value is value of last expression.

387 Precedence and Associativity
What is this code trying to do? Is it correct? if (a << == c & 0xf) { … } Actual precedence: if (((a << (1 + 1)) == c) & 0xf) { … } Precedence is which operator gets applied first. x*y + a The * has higher precedence, so the y operand is bound to the *. Associativity is which operator gets executed first when the precedence is the same. a + b + c (a + b) + c a – b + c (a – b) + c a + b – c (a + b) – c a/b/c (a/b)/c i = j = k = 1234; ((i = j) = k) = 1234; i = (j = (k = 1234)); // This is correct.

388 a + b + c Tip: Look at the operand, not the operator.
A left associative operator means that an operand goes with the operator on the left. Suppose (hypothetically) that + was left associative and – was right associative. Then how would this be evaluated? a – b + c a + b + c Trick question: The associativity of operators of equal precedence cannot conflict.

389 Baking Recipe Prepare mixture A (takes 10 minutes). Let stand 50 minutes. Prepare mixture B (takes 20 minutes). Prepare mixture C (takes 30 minutes). Let stand 20 minutes. Whip B and C together producing D. Gently fold A into D. Expression: cake = make_A() FOLD make_B() WHIP make_C() Precedence? Expression: cake = make_A() FOLD ( make_B() WHIP make_C() ) Best preparation order? A B C FOLD WHIP Best preparation order is A, C, B. Evaluation order is analagous to what order you prepare A, B, and C. You can prepare A first, even though it is used last in the expression.

390 Evaluation Order [Show eval_order.] Consider: How does this associate?
int &f(int i) { cout << i << endl; static int x; return x; } How does this associate? f(1) = f(2) = f(3); f(1) = (f(2) = (f(3) = 1234)); What gets printed? Associativity does not mean evaluation order! Associativity and precedence are about grouping. Why allow this? A few operators guarantee evaluation order. Evaluation order is not specified. (e1) + (e2) + (e3) If no side effects, evaluation order cannot be detected anyway. Regardless, associativity is not the same. You could have left associative, but right-to-left evaluation order. Evaluation order is left up to the compiler so it can make the best decisions as to performance. This is the same as for baking. See eval_order directory for code example.

391 These two will print what?
Given: int &f(int i) { cout << i << endl; static int x; return x; } These two will print what? f(1) = f(2) = f(3); func(f(1), f(2), f(3)); On Linux, g++: 1, 2, 3 and 3, 2, 1 On Linux, clang++: 3, 2, 1 and 1, 2, 3 On Sun: 1, 2, 3 and 1, 2, 3

392 Sequence Points What is the value of i? More examples:
int i = 0; ++i = i++ + i++; Linux with g++: 4 Solaris with g++: 2 Solaris with Sun: 1 More examples: i++ / i-- i = a[i++]; f(i++, i++) Usually only a problem when you have side effects. C/C++ defines sequence points. All side effects are executed at sequence points. You cannot read or write to the same lvalue multiple times between sequence points, or else you get undefined behavior. Eval order of those is not defined, so those are undefined.

393 What are some sequence points?
End of statement Function call, beginning and end Comma operator a = 1, b = 2; Short-circuiting logical operator: && and || Eval order of those is not defined, so those are undefined.

394 Casts Syntax is cast<T>(…), but it is not a template. Syntax just kind of makes it look like one. static_cast Used for casts that are “reasonable”, but not implicit. Like a cast from void *. Loss of precision casts. const_cast Only used to change the const-ness of something. dynamic_cast Cover later reinterpret_cast Used to do things that seem to make no sense. C-style (T) val T (val) Anything to anything. Precedence is very high, right after () (function call), [], ->, and . (member access).

395 const_cast can useful where you want to leverage something that returns const reference to something that you know is not actually const. const string & shorterString(const string &, const string &); string & shorterString(string &, string &) { return const_cast<string &>( shorterString( const_cast<const string &>(s1), const_cast<const string &>(s2) ) ); }

396 Statements

397 Simple and Compound The simplest statement is just a ;
A compound statement is enclosed in curly braces, with no terminating ; { s1; s2; } Often useful for short, temporary, copy-and-paste code. // Not copyable. int i = ...; double x = ...; // ... Some statements here. // Copyable. { int i = ...; double x = ...; // ... Some statements here. }

398 Declaration Statement
Can be placed anywhere, not just at the top of a block. We call it a declaration statement, but it is usually also a definition. extern int i; int j; What is point of declaration? [Show point_of_declaration/] class A { int i, k; … }; void A::func(int i) { int ii = i; int k = k, k2 = k; … } Definitions cause storage to be allocated.

399 if Statement You can have a declaration/definition in it.
if (Obj *p = lookup(...)) { … } What does this print? int n = -1; if (n >= 0) for (int i = 0; i < 4; i++) if (i%2 == 0) printf(“%d is even.\n“, i); else printf(“n is negative.\n"); The else is attached to the nearest if. Defensive programming: always use curly braces.

400 switch statement Integral Expression Constant label Exit switch switch (e) { case 1: stmt1; break; case 3: stmt2; case 4: case 5: stmt3; break; default: break; // Redundant. } Fall through Fall through

401 How is this code? enum State { START, STATE1, STATE2 } state; while (not_done) { switch (state) { case START: break; case STATE1: break; case STATE2: break; } } If you make a careless mistake and don’t have all the cases, then it will be hard to debug. As a form of defensive programming, you should have a default case that just fails always, to tell you immediately that you have a bug. Also, since uninitialized variables are commonly 0, it is better to write code that will fail quickly if a variable that should be initialized starts with 0. Thus, make the first enum be 1. If you do both of these, then you will likely catch the fact that you forgot to initialize the state variable.

402 Better: enum State { START = 1, STATE1, STATE2 } state; while (not_done) { switch (state) { case START: break; case STATE1: break; case STATE2: break; default: assert(false); abort(); break; } } This has greater chance of alerting you to something wrong.

403 Correct: enum State { START = 1, STATE1, STATE2 } state = START; while (not_done) { switch (state) { case START: break; case STATE1: break; case STATE2: break; default: assert(false); abort(); break; } } This has greater chance of alerting you to something wrong.

404 This is a jump across an initializer, and won’t compile.
Does this compile? switch (e) { case 1: int i = 1; // … Some code here… break; case 3: // … Some other code here… break; default: assert(false); abort(); break; } This is a jump across an initializer, and won’t compile. What’s the good practice to be learned? Use blocks, as shown on the next page.

405 Use blocks: switch (e) { case 1: { int i = 1; // … Some code here… } break; case 3: { // … Some other code here… } break; default: assert(false); abort(); break; }

406 General syntax: switch (expr) { int i; // Any sequence of any type of statements, // including further compound statements, flow // control, etc. // Case labels may be interspersed. } switch (expr) { int i, j; if (cond) { some_statement1; case 1: some_statement2; stmnt2; } else { case 3: some_statement3; } default: some_statement4; } Show odd_switch/

407 Which is more efficient?
if (a == 900) { … } else if (a == 2) { … } else if (a == 93) { … } else { … } switch (a) { case 900: …; break; case 2: …; break; case 93: …; break; default: …; break; } [Show switch_jump/] About the same. In each case, there will need to be a sequence of comparisions.

408 Which is more efficient?
if (a == 1) { } else if (a == 2) { } else if (a == 3) { } else if (a == 4) { } else if (a == 5) { } else { } switch (a) { case 1: …; break; case 2: …; break; case 3: …; break; case 4: …; break; case 5: …; break; default: …; break; } The switch will usually be faster, since the compiler will replace the sequence of comparisons with some kind of vectored or table driven jump.

409 for Statement for (int i = 0, j = 0; j < 0; j++, i += 2) { if (some_cond) { j = 0; } } // Search for something in a loop. for (int i = 0; i < v.size(); i++) { if (v.at(i) == what_we_are_looking_for) { break; } } How can one tell whether or not we found what we were looking for? C++11: pos = find_if(std::begin(array), std::end(array), [&](const MyClass &o) { … }); It’s subjective decision as to whether or not i is visible. In an early version of C++, it was. Now it is not. This means that you can’t test it after the for loop to see whether or not you hit the end of the loop.

410 Range-based for (C++11):
list<MyClass> a_list; for (auto it : a_list) { … } for (list<MyClass>::iterator it = a_list.begin(); it != a_list.end(); ++it) { … } int a[] = {1, 2, 3, 4}; for (int &i : a) { if (i == 3) { i++; } } #include <initializer_list> for (int i : {1, 2, 3, 4}) { … }

411 while Statement Can also have a declaration in it.
while (int i = j – 3) { … }

412 do-while Statement do { … } while (expr);
Used for multi-statement macros. #define m(…) \ do { \ … \ } while (false)

413 break and continue Statements
break will immediately exit the nearest enclosing loop. continue will immediately re-test and possibly continue the nearest enclosing loop.

414 Can this code be simplified?
bool keep_going = true; while (keep_going) { if (...) { keep_going = false; } else { } } Simplification: while (true) { if (...) { break; } }

415 goto Statement goto label; label: statement; Is it a good idea?
How would you rewrite this? while (expr1) { while (expr2) { if (expr3) { goto exit; } … } … } exit: Also have jump over initialization issues. Whether or not goto’s are good is very subjective. Rewriting the while loop without a goto makes it more complex and harder to understand, in my opinion.

416 What about this? again: Connection c(url); if (!c.connect()) { goto again; } Jumping backwards is okay. It calls the destructor and constructs again.

417 What and Why Regarding Initialization of Variables
Consider two kinds of variables: int g; void f() { int i; } When did their lifetime start? Where is the memory for each? What was there before? [Show initialization_rationale]

418

419 Start here, Spring 2018

420 Multiple Inheritance and Virtual Inheritance

421 Casting To and From void *
Casts are dumb. They often don’t require any machine instructions. They just change the type of a pointer. Consider: struct A { int i; }; struct B { char buf[10]; int i; }; A *ap = new A; ap->i = 1234; B *bp = (B *) ap; // Are bp and ap actually different addresses? cout << ap->i << endl; cout << bp->i << endl; It’s actually not always true that they are dumb, but we’ll live with that fiction for now.

422 Consider: class Base {… }; class Derived : public Base { … }; void foo(void *vp) { Base *bp = (Base *) vp; } Derived *dp = new Derived; foo(dp); // Okay? (Derived *) (void *) dp == dp; // ? Base *bp = dp; (Base *) (void *) dp == bp; // ? [Show void_star_cast.] void * can only be used when you will cast back to the exact same type.

423 Multiple Inheritance Do we have it in the real world?
A class in C++ can inherit from multiple base classes. Members with same name do not conflict unless they are used in an ambiguous way. An attempt to call, from the derived class, a method that is defined in both base classes would be ambiguous, for example. Scope resolution operator (::) can be used to disambiguate.

424 What functions are available at D?
struct B1 { void foo1(); … }; struct B2 { void foo2(); … }; struct B3 : public B1, public B2 { void foo3(); … }; struct B4 { void foo4(); … }; struct D : public B3, public B4 { void foo5(); … }; B1: void foo1(); B2: void foo2(); B3: void foo3(); B4: void foo4(); D: void foo5(); What functions are available at D?

425 What might the layout of a derived object look like in single inheritance and no polymorphism? Consider: class A {…}; class B : public A {…}; class C : public B {…}; What is needed to upcast or downcast? What do you expect the following code print? C c; C *cp = &c; B *bp = &c; A *ap = &c; printf(“C: %p, B: %p, A: %p\n”, (void *) cp, (void *) bp, (void *) ap); C part B part A subobject B sub- object Complete C object The A subobject begins at address 16

426 B1 subobject begins at address 16
What does the layout of a derived object look like in multiple inheritance? Consider: struct B1 { int b1; }; struct B2 { int b2; }; struct B3 : public B1, public B2 { int b3; }; struct B4 { int b4; }; struct D : public B3, public B4 { int d; }; B3 sub- object B4 sub- object B3 part: b3 B2 subobject: b2 B1 subobject: b1 B4 subobject: b4 D part: d Complete D object B1 subobject begins at address 16 B1 b1 B2 b2 B3 b3 B4 b4 D d

427 B1 subobject begins at address 16
What is needed to upcast or downcast? What does the following code print? D d; B1 *b1p = &d; B2 *b2p = &d; B3 *b3p = &d; B4 *b4p = &d; printf(“D:%p, B1:%p, B2:%p, B3:%p, B4:%p\n”, (void *) &d, (void *) b1p, (void *) b2p, (void *) b3p, (void *) b4p); B3 sub- object B4 sub- object B3 part: b3 B2 subobject: b2 B1 subobject: b1 B4 subobject: b4 D part: d Complete D object B1 subobject begins at address 16 B1 b1 B2 b2 B3 b3 B4 b4 D d

428 Revisit casting to/from void *:
D *dp = 16; void *vp = (void *) 16; (B4 *) vp ==? what address; (B4 *) (void *) dp ==? (B4 *) dp; B3 sub- object B4 sub- object B3 part: b3 B2 subobject: b2 B1 subobject: b1 B4 subobject: b4 D part: d Complete D object B1 subobject begins at address 16 B1 b1 B2 b2 B3 b3 B4 b4 D d

429 What if we chose the initializer list order?
What order are base class constructors called? struct B1 { B1(B2 *); … }; struct B2 { … }; class D : public B1, public B2 { … }; D::D() : B2(…), B1(this) { // Okay? … } What if we chose the initializer list order? Different constructors might list differently. Some constructors might not list at all. Base class constructors are called in the order that they are listed on the inheritance line.

430 Which foo() gets called?
struct B1 { void foo(); }; struct B2 { void foo(); }; struct D : public B1, public B2 {}; D d; // Does this call B1::foo() or B2::foo()? d.foo(); The call to d.foo() is ambiguous.

431 Ambiguous or not? class B1 { private: void foo(); }; class B2 { public: void foo(); }; struct D : public B1, public B2 {}; D d; // Does this call B1::foo() or B2::foo()? d.foo(); It’s still ambiguous. To avoid having subtle semantic changes due to seemingly unrelated access changes, the designers decided to make name resolution happen before access resolution.

432 Which foo() gets called?
struct B1 { void foo(int); }; struct B2 { void foo(double); }; struct D : public B1, public B2 {}; D d; d.foo(12); // What happens? Still ambiguous. Name resolution happens before overload resolution.

433 Implicit conversions to a base class pointer may be ambiguous:
struct B1 { … }; struct B2 { … }; struct D : public B1, public B2 { … }; extern foo(const B1 &); extern foo(const B2 &); D d; foo(d); // What happens? This is also ambiguous, since it doesn’t know which to use.

434 The ambiguity is “latent”.
struct B1 { void foo(); }; struct B2 { void foo(); }: struct D : public B1, public B2 { void goo(); }; D d; // Is this okay? d.foo(); // Is this okay? Error doesn’t happen unless/until you actually try to call an ambiguous function.

435 Resolving Ambiguities
You can always use a scope resolution operator. struct B1 { void foo(); }; struct B2 { void foo(); }: struct D : public B1, public B2 {}; D d; d.B1::foo(); But in this case, the user must know which one is correct. Better for class designer to resolve it by forwarding. struct B1 { void foo(); }; struct B2 { void foo(); }: struct D : public B1, public B2 { // Forwarding function. void foo() { B1::foo(); } }; D d; d.foo();

436 Or use a using declaration.
struct B1 { void foo(); }; struct B2 { void foo(); }: struct D : public B1, public B2 { // Using declaration. using B1::foo; }; D d; d.foo();

437 Virtual Functions Multiple inheritance can be used with virtual functions. The actual function called is determined by following the inheritance tree downwards from the type used in invocation. B *b = …; b->func(); // If func is virtual, follow the // tree down from B.

438 class B1 { public: virtual void foo1(); virtual void foo(); }; class B2 { public: virtual void foo2(); virtual void foo(); }; class D : public B1, public B2 { public: virtual void foo1(); virtual void foo2(); virtual void foo(); }; D d; B1 &b1(d); B2 &b2(d); b1.foo1(); // Calls ? b2.foo2(); // Calls ? b1.foo(); // Calls ? B1 B2 D b1.foo1(); // Calls D::foo1(); b2.foo2(); // Calls D::foo2(); b1.foo(); // Calls D::foo();

439 A1: foo() = 0 A2: foo() = 0 B1: foo() B2: foo() C
class A1 { public: virtual void foo() = 0; }; class A2 { public: virtual void foo() = 0; }; class B1 : public A1 { public: virtual void foo(); }; class B2 : public A2 { public: virtual void foo(); }; class C : public B1, public B2 {}; C c; c.foo(); // Okay? static_cast<A1*>(&c)->foo(); // Calls which foo()? static_cast<A2*>(&c)->foo(); // Calls which foo()? A1: foo() = 0 A2: foo() = 0 B1: foo() B2: foo() C

440 Pure Virtual Functions (Abstract Base Class)
MI can be mixed with pure virtual functions. For a class to be instantiable, all PVFs must be defined. class B1 { public: virtual void f1() = 0; }; class B2 { public: virtual void f2() = 0; }; class D : public B1, public B2 { public: virtual void f1() {} }; class E : public D { public: virtual void f2() {} }; D d; // Okay? E e; // Okay? B1 B2 D D d; // Not okay, since f2() not defined. E e; // Okay, since all PVF defined. E

441 MI with Repeated Base Class
How many A subobjects in a C object? struct A { int i_var; }; struct B1 : public A {}; struct B2 : public A {}; struct C : public B1, public B2 {}; A A A subobject B1 sub- object B1 part Complete C object B1 B2 A subobject B2 sub- object B2 part c.i_var is ambiguous. C C part Which i_var does this access? C c; c.i_var; // Which i_var?

442 Virtual Base Classes The subobjects are different, unless the base class is virtual. This is an overloading of the use of the term virtual. Example class A { public: int a; }; class B1 : virtual public A {}; class B2 : virtual public A {}; class C : public B1, public B2 {}; C c; c.a; // Only one copy.

443 How many A subobjects in a C object?
class A { public: int a_member; }; class B1 : virtual public A { int b1;}; class B2 : virtual public A { int b2; }; class C : public B1, public B2 { int c; }; A B1 sub- object B1 B2 A subobject B1 part Complete C object B2 sub- object C B2 part C part Diamond inheritance No more ambiguity C c; c.a_member; // Which a_member?

444 A B1 sub- object A subobject B1 B2 B1 part Complete C object
Address 0 A B1 sub- object A subobject B1 B2 B1 part Complete C object B2 sub- object B2 part C C part Address 0 A A subobject Note that the increment between the base class and the rest of the class changes. B0 sub- object B1 sub- object B0 part B0 B1 B2 Complete C object B1 part B2 sub- object B2 part C C part

445 [Show vi_perf.] [Show vb_cast.]
Under virtual inheritance, a cast may be a function call (or at least some computation).

446 Construction under Virtual Inheritance
Okay? struct A { A(const char *l) { … } }; struct B1 : public A { // Non-virtual B1() : A(“B1”) {} }; struct B2 : public A { // Non-virtual B2() : A(“B2”) {} }; How about this? struct C : public B1, public B2 {};

447 What parameter is passed to the constructor of A?
Okay? struct A { A(const char *l) { … } }; struct B1 : virtual public A { // Virtual B1() : A(“B1”) {} }; struct B2 : virtual public A { // Virtual B2() : A(“B2”) {} }; How about this? struct C : public B1, public B2 {}; What parameter is passed to the constructor of A?

448 The most derived type must call the ctor:
struct A { A(const char *l) { … } }; struct B1 : virtual public A { // Virtual B1() : A(“B1”) {} }; struct B2 : virtual public A { // Virtual B2() : A(“B2”) {} }; struct C : public B1, public B2 { C() : A(“C”) {} };

449 Other Patterns Can have other different inheritance patterns: A A A A
B1 B2 B1 B2 B3 B4 C C

450 Research/Projects High performance computing Data Science
Parallel discrete event simulation on many core architectures Think Civ type simulations Leveraging RDMA/Infiniband Data Science Algal recognition Remote sensing Viral metagenomics: Pick out viral characteristics from metagenomics sequencing Product quantization and/or locality sensitive hashing for high-dimensional approximate k-NN

451 Templates

452 Introduction How is a template different from normal source code?
void foo() {…} template <typename T> void foo1() {…} Template definitions are not really “compiled”. Just cause things to be remembered.

453 Templates Are Like Super Macros
Where do you put templates, header files or implementation files? Every use must be preceded in the same translation unit by its definition. Private template could be in an implementation file.

454 A template is just that, a template or “pattern”.
Essentially a macro on steroids. Normal source code is compiled on a one-to-one basis. void f(int i) { … } f: save_regs ld r0, sp(0) add ret One function in source code One function assembly language Compilation

455 A single template serves as a pattern, so it can be used multiple times to create multiple “instantiations”. One function in source code template <typename T> void f(T i) {… } Compilation & instantiation f: save_regs ld r0, sp(0) add 4 ret f: save_regs ld r0, sp(4) add 8 ret f: save_regs ld r0, sp(8) add 16 ret Multiple functions in assembly language

456 Compilation Instantiation
f<char>: save_regs ld r0, sp(0) add 4 ret void f(char i) {…} template <typename T> void f(T i) {…} f<int>: save_regs ld r0, sp(4) add 8 ret void f(int i) {…} f<double>: save_regs ld r0, sp(8) add 16 ret void f(double i) {…}

457 What happens when compiler encounters a normal function definition
What happens when compiler encounters a normal function definition? A template definition? void foo() {…} // Normal function. template <typename T> void foo1() {…} Template definitions are not directly “compiled” when encountered by the compiler. Just cause things to be remembered, for later instantiation. When the compiler encounters a normal function definition, it will cause assembly language code to be emitted. When a compiler encounters a template definition, it just remembers it.

458 Templates and Classes as Models
Both are “models”. (What is a model?) Classes are models for objects. An object: Has source code (member functions). Has data (member variables). But source code never differs. Data layout never differs. Only the values differ. Templates go back one step. They are models for classes or code. You first instantiate a template to fix a particular source code and data layout. The source code of different instantiations can differ. The data layout of different instantiations can differ. Sometimes known as parametric polymorphism (compared to subtype polymorphism).

459 Example Suppose you want to define a function to find the minimum of two integers: int min(int i1, int i2) { return i1 < i2 ? i1 : i2; } Now how about for doubles: double min(double x1, double x2) { return x1 < x2 ? x1 : x2; } Now how about long longs… Solution? You could use a macro, but a template is a better solution.

460 Could use a macro: Disadvantages?
#define min(x, y) \ ((x) < (y) ? (x) : (y)) Disadvantages? Side effects, due to double evaluation. Hard to debug, especially for multiline. Code bloat, since they are inherently inlined.

461 g++ standard library definition:
Function template: template <typename T> // Same as “template <class T>” T min(T v1, T v2) { return (v1 < v2) ? v1 : v2; } Is this right? g++ standard library definition: template<typename _Tp> inline const _Tp& min(const _Tp& __a, const _Tp& __b) { if (__b < __a) return __b; return __a; } Why the underscores? The first definition of min is not really wrong, but can be improved by making the input const ref, and also the return const ref. The g++ version uses underscores so that there will be no conflict with any user defined macro. Users are not supposed to define macros, or use any symbols that have leading underscores.

462 Five Kinds Alias templates Function templates Class templates
Patterns for type aliases (typedefs). template <typename T> using ptr = T *; ptr<int> ip; // ip is a pointer to an int. Function templates “Patterns” for free-standing functions template <typename T1, typename T2> void f(const T1 &o1, const T2 &o2) { … } Class templates “Patterns” for an entire class, including member functions and member variables. template <typename T1, typename T2> class A { … };

463 Variable templates (C++14)
Member templates “Patterns” for a single member function inside a class (which may or may not be a templated class). struct A { template <typename T1> void f(const T1 &) {…} … }; Variable templates (C++14) A way to parameterize a variable. template <typename T> T var = 3.14; template <> int var<int> = 1; int main() { std::cout << var<int> << std::endl; std::cout << var<double> << std::endl; }

464 Template Parameters Each template has a list of template parameters. When the template is actually used, the parameters are filled in with real things, and an actual function or class is generated. This is known as an instantiation. template <typename T1, typename T2> void func(); In normal functions, each parameter is replaced with a value (of a specific type). void print_int(int i) { cout << i; } In templates, each parameter is replaced with a type, or a constant (for non-type parameters). template <typename T> void print(T o) { cout << o; } After instantiation, it is a function: void print<MyClass>(MyClass o) { cout << o; }

465 In normal functions, the argument to the parameter is specified explicitly in the argument list.
void foo(int p1, double p2, const char *p3); foo(123, x, “hello”); For template parameters, the replacement can be specified explicitly, as in functions, or, for function templates, they can be deduced. template <typename T1, typename T2> void f(T1, T2); f(123, 3.14); f<double, double>(123, 3.14);

466 A non-type parameter works like a constant value.
Inside a template, a template type parameter works just like any C++ type. template <typename T> void f(T v) { T temp; } A non-type parameter works like a constant value. template <int N> void f() { int i = N; printf(“%d\n”, N); } The type parameter can be used as any other type: struct A { typedef int foo; struct N { … }; }; template <typename T> void func(T v) { typename T::foo i; // Define an int. typename T::N n; // Define an N object. … }

467 Non-Type Parameters These look like ordinary parameter declarations, but must be instantiated with a constant value. template <int N> … template <char *s> … Why might this be useful? template <typename T, int N> void sort(T (&a)[N]) { T save[N]; if (1 < N) {…} } Will the if stay in the compiled code? The ‘if’ will not be included in the compiled code, because the truth of falsity of the if statement will be known at compile time. N will be replaced by a constant.

468 Another example: template <const char *pref> void msg(const char *m) { cerr << pref << ": " << m << endl; } extern const char warning_s[] = "Warning"; void (&warn)(const char *) = msg<warning_s>; extern const char error_s[] = "Error"; void (&error)(const char *) = msg<error_s>; int main() { warn("Danger, Will Robinson!"); error("That does not compute."); }

469 Variadic Templates In C++11, templates can have varying number of template and function arguments. template <typename... Ts> void f(Ts... params) { … } Example, a variadic function template to sum its args: template <typename T> const T &sum(const T &v) { return v; } template <typename T, typename... Ts> T sum(const T &v, const Ts & ... params) { return v + sum(params...); } … sum(1); sum(1, 2); sum(1.1, 2.2, 3.3); sum(1, 2.2, 3.3); // What’s wrong with this? BigInt bi1, bi2, bi3; sum(bi1, bi2, bi3, 1); sum(1, bi1); // Okay?

470 You can get the number of parameters in the pack with sizeof...:
template <typename... Ts> void foo(const Ts & ... params) { cout << sizeof...(Ts) << endl; cout << sizeof...(params) << endl; }

471 Class templates can also be variadic:
template <typename... Ts> class A { … }; Non-type parameters can also be variadic: template <size_t... Ns> class A { … };

472 Expansion can be a pattern:
template <typename T> const T &out(const T &v) { cout << v << endl; return v; } template <typename... Ts> void pass(const Ts &... params) {} template <typename... Ts> void foo(const Ts & ... params) { pass(out(params)...); } foo(1, 1.3); expands to pass(out(1), out(1.3)); Okay? pass((out(params), 1) ...); pass((out(params), 1) ...); // Okay.

473 Alias templates

474 template <typename T> using ptr = T
template <typename T> using ptr = T *; ptr<int> p; // Defines a pointer to an int. template <typename T> typedef T *ptr; // Syntax error. template <typename T> using my_map_t = std::map<T, vector<T>>; my_map_t<std::string> m1; my_map_t<const char *> m2;

475 Function Templates

476 Function templates are “patterns” for freestanding functions.
Allow a function to be parameterized by type. The syntax is: template <typename T1, typename T2> void f(…) { … } Inside the function body, and in the parameter list, you can use T1, etc. Example: template <typename T1, class T2> T1 f(const T2 &p1, const T2 p2, T2 (*fp)(T1)) { T1 t1; T2 t2; … return t1; } Can be used multiple times.

477 More examples: template <typename T> const T &min(const T &a, const T &b) { return a < b ? a : b; } i = min(j, k); std::string first, s1(…), s2(…); first = min(s1, s2);

478 Will the above work for all classes? If not, how can it be improved?
Another example: template <typename T, int size> T min(T (&array)[size]) { T m = array[0]; for (int i = 1; i < size; i++) { if (array[i] < m) { m = array[i]; } } return m; } Will the above work for all classes? If not, how can it be improved? template <typename T, int size> const T &min(T (&array)[size]) { int min_index = 0; for (int i = 1; i < size; i++) { if (array[i] < array[min_index]) { min_index = i; } } return array[min_index] } The first definition will not work for all classes, because not all classes will have a defined copy constructor. Note that the definition of ‘m’ is actually using the copy constructor, not the assignment operator. The second definition does not require that the class have a copy constructor.

479 Instantiation Instantiation is the process of actually filling in the template parameters, and constructing the actual function. Occurs implicitly either when a function is called, or when a function address is taken. template <typename T> void foo(T) {…} foo(1); void (*fp)(double) = &foo; During instantiation, the template parameters must be bound to actual template arguments, during a process know as template argument deduction. Compile time or run time? Template argument deduction must happen at compile time, since templates are instantiated at compile time.

480 Template Argument Deduction
Deduction is more complicated than a simple replacement. (But luckily the compiler does it.) template <typename T> sort(MyList <T> &a); MyList<int> a1; MyList<double> a2; sort(a1); // What is T? sort(a2); // What is T? template <typename T> void func_tmpl1(T (*f)(int)); double f1(int); func_tmpl1(&f1); // What is T? template <typename T> sort(MyList <T> &a); MyList<int> a1; MyList<double> a2; sort(a1); // T is int. sort(a2); // T is double. template <typename T> void func_tmpl1(T (*f)(int)); double f1(int); func_tmpl1(&f1); // T is double. Note that you can’t overload based on return type, but you can use it for deduction.

481 template <typename T> void func_tmpl2(int (
template <typename T> void func_tmpl2(int (*fp)(T)); int f1(double); int f2(char *); double f3(double); func_tmpl2(f1); // What is T? func_tmpl2(f2); // What is T? func_tmpl2(f3); // What is T? template <typename T1, typename T2> void func_tmpl3(T1 (*fp)(T2)); int f1(double); double f2(char *); func_tmpl3(f1); // What is T1, T2? func_tmpl3(f2); // What is T1, T2? template <typename T> void func_tmpl2(int (*fp)(T)); int f1(double); int f2(char *); double f3(double); func2(f1); // T is double. func2(f2); // T is char *. func_tmpl2(f3); // Fails. template <typename T1, typename T2> void func_tmpl3(T1 (*fp)(T2)); int f1(double); double f2(char *); func3(f1); // T1 is int, T2 is double. func3(f2); // T1 is double, T2 is char *.

482 Do these compile? template <typename T> const T &min(const T &, const T &); … min(1, 3.14); min(1, 2U); template <typename T> T min2(T *, int size); … int a[4] = {1, 2, 3, 4}; min2(a, 4); template <typename T> T min3(const T *, int size); … min3(a, 4); template <typename T> const T &min(const T &, const T &); … min(1, 3.14); // Ambiguous. min(1, 2U); // Ambiguous. template <typename T> T min2(T *, int size); … int a[4] = {1, 2, 3, 4}; min2(a, 4); // Okay, since this is lvalue to rvalue. template <typename T> T min3(const T *, int size); … min3(a, 4); // Okay, since a const can be added. The main point is that only a few conversions are considered during template argument deduction.

483 How about these? template <typename T> void func(T *p1, T *p2); class A {…}; class B : public A {…}; A a; B b; func(&a, &b); // Okay? func(&a, static_cast<A *>(&b)); // Okay? template <typename T> void func(Container<T> &); template <typename T> class FancyContainer : public Container<T> { … }; FancyContainer<int> fc; func(fc); Conversions from derived to base also happen, as long as the base is a template in some cases. template <typename T> void func(T *p1, T *p2); class A {…}; class B : public A {…}; A a; B b; func(&a, &b); // Ambiguous, since base is not a template. func(static_cast<B *>(&a), &b); // Okay, since resolving with static cast. template <typename T> void func(Container<T> &); template <typename T> class FancyContainer : public Container<T> { … }; FancyContainer<int> fc; func(fc); // Okay, since base is a template.

484 template <typename T> void func(T. , T
template <typename T> void func(T *, T *); template <typename T> class B {}; class D : public B<int> {}; B<int> b; D d; func(&b, &d); Ambiguous.

485 Arrays are converted if the parameter is not a reference:
template <typename T1, typename T2> void foo(T1 p1, T2 &p2) { … } … int a[10]; foo(a, a); T1 is deduced to be int *, but T2 is deduced to be int [10].

486 Most conversions are not applied for template argument deduction
Most conversions are not applied for template argument deduction. Only conversions that are used are: Lvalue to rvalue, such as array to pointer. template <typename T> void f(T *); int a[] = {1, 2}; f(a); Unless the parameter T is a reference! Qualification conversion, such as adding const. template <typename T> void f(const T *); double x; f(&x); Derived pointer or reference to base class pointer or reference, if the classes are class templates. template <typename T> void f(A<T> *); template <typename T> class B : public A<T> { … }; B<int> b; f(&b);

487 Remember, there are two steps:
Note that even though usual conversions are not applied to template arguments, they are applied to non-template arguments. template <typename T> void func(T v1, T v2); func(1, 1.23); // Okay? template <typename T> void func2(T v1, double v2); func2(1, 123); // Okay? Remember, there are two steps: Template argument deduction Overload resolution. Template deduction happens first! template <typename T> void func(T v1, T v2); func(1, 1.23); // Not okay, since both parameters are template. template <typename T> void func2(T v1, int v2); func2(1, 2.3); // Okay (with warning), since the second one is not template.

488 The return type of the function template is not deduced
The return type of the function template is not deduced. (Also not used for overload resolution.) template <typename T> T f(); int i = f(); // Okay? template <typename T> T f2(T *); double x = f2((int *) nullptr); // Okay? template <typename T> T f3(T); double x = f3(1); // Okay? template <typename T> T f4(T, T); double x = f4(1, 1.3); // Okay? Note that the return type of a function in a parameter is deduced, however. template <typename T> T f(); int i = f(); // Not okay. template <typename T> T f2(T *); double x = f2((int *) 0); // Yes, since only one T can match (T is an int). template <typename T> T f3(T); double x = f3(1); // Yes, T is int. template <typename T> T f4(T, T); double x = f4(1, 1.3); // Ambiguous.

489 Deduction procedure is as follows:
Each function parameter is examined to see if it includes a template parameter. If so, a match is attempted, normal conversions are not used on template arguments. They are used on non-template arguments. If a template parameter is in more than one function parameter, then the deduced template argument must be the same in each function parameter. There are lots of complicated rules. Usually you only need to look into them when the compiler does the “wrong” thing: It thinks the situation is ambiguous, but you don’t. It deduces arguments differently from what you think it should.

490 Explicit Arguments Most of the time, you want the compiler to deduce the arguments. template <typename T> T min(T v1, T v2); min(1, 2); min(1, 3.14); // Okay? template <typename T> T f(); int i = f(); // Okay? Solution is to use explicit arguments min<double>(1, 3.14); min<int>(1, 3.14); int j = f<int>(); // Okay? template <typename T> T min(T v1, T v2); min(1, 2); min(1, 3.14); // Ambiguous. template <typename T> T f(); int i = f(); // Can’t use return type, so ambiguous. int j = f<int>(); // This is okay.

491 Suppose we have a function sum(), what should we use for the return type?
template <typename T1, typename T2> T1 sum(T1, T2); short s; sum(s, 1); sum(1, s); // Okay? template <typename T1, typename T2> T2 sum(T1, T2); short s; sum(s, 1); sum(1, s); // Okay? No good options in C++98. Any choice will not work, so the solution is to use an explicit argument. template <typename RT, typename T1, typename T2> RT sum(T1, T2); sum<int>(s, 1); sum<int>(1, s); All possibilities in the first group of examples will have the possibility that the return type is ‘short’, which is too small to hold the sum.

492 If using C++11, we can use decltype.
template <typename T1, typename T2> auto sum(T1 v1, T2 v2) -> decltype(v1 + v2); sum(s, 1); sum(1, s); Possible without trailing return type, but ugly: template <typename T1, typename T2> decltype(*(T1 *) nullptr + *(T2 *) nullptr) sum(T1 v1, T2 v2); sum(s, 1); sum(1, s); template <typename T1, typename T2> decltype(std::declval(T1) + std::declval(T2)) sum(T1 v1, T2 v2); In C++14, can use auto: template <typename T1, typename T2> inline auto sum(const T1 &v1, const T2 &v2) { return v1 + v2; }

493 Instantiation, and template argument deduction also happens when the address of a function is taken. What happens here? template <typename T, int size> const T &min(T (&arr)[size]) { … } typedef int (&array_int)[10]; typedef double (&array_double)[20]; void func(int &(*)(array_int)); void func(double &(*)(array_double)); … func(&min); To solve: func(static_cast<double &(*)(array_double)>(&min)); func(&min<int, 10>); The problem is that because there are two versions of func(), the compiler doesn’t know how to instantiate min(). So the solution is to use a cast, to tell the compiler which one you want, or to use explicit template arguments.

494 Perfect Forwarding What if we want to write a template as generally as possible, but also need to call some other functions: // For non-rvalues. template <typename T> void foo(const T &o) { goo(o); } // For rvalues. template <typename T> void foo(T &&o) { goo(std::move(o)); }

495 This is obviously rather unmanageable.
What if we gave more parameters, each of which may either be movable or not: template <typename T> void foo(const T &o1, const T &o2) { goo(o1, o2); } template <typename T> void foo(T &&o1, const T &o2) { goo(std::move(o1), o2); } template <typename T> void foo(const T &o1, T &&o2) { goo(o1, std::move(o2)); } template <typename T> void foo(T &&o1, T &&o2) { goo(std::move(o1), std::move(o2)); } This is obviously rather unmanageable.

496 Two part solution: First, create special deduction rules for rvalue reference parameters.
template <typename T> void foo(T &&o) { … } T is A & if called on lvalue of type A. T is A if called on an rvalue of type A. T is const A & if called on const lvalue of type A. So: A a; const A a2; foo(a); // T is A &. foo(get_an_A()); // T is A. foo(a2); // T is const A &.

497 Then, we need special rules for “collapsing” references. Rules are:
If T is A &, then what is T &&? Rules are: A& & becomes A& A& && becomes A& A&& & becomes A& A&& && becomes A&& So, we can then write: template <typename T> void foo(T &&o1, T &&o2) { goo(static_cast<T&&>(o1), static_cast<T&&>(o2)); } This is done often enough such that there is a standard function for it: std::forward. template <typename T> void foo(T &&o1, T &&o2) { goo(std::forward<T>(o1), std::forward<T>(o2)); }

498 Specialization Consider: What happens when you call?
template <typename T> T min(T a, T b) { return (a < b) ? a : b; } What happens when you call? min(“a”, “b”) Sometimes, the generic definition of a template is not right. Explicit specializations can then be used. template <> const char * min<const char *>(const char *s1, const char *s2) { return strcmp(s1, s2) < 0 ? s1 : s2; } The definition of min() near the top is fine, but doesn’t work when called with string literals. What ends up happening is that the address of the string literals are compared, which is almost certainly not what is desired. So to make it work right, need to create a special version of min() that is used when comparing const char *.

499 Does this work? template <typename T> T min(T a, T b) { return (a < b) ? a : b; } int main() { const char *first = min(“a”, “b”); } template <> const char * min<const char *>(const char *s1, const char *s2) { return strcmp(s1, s2) < 0 ? s1 : s2; } Does not work, because the declaration of a specialization must be seen before instantiation.

500 At least declaration must be seen first.
template <typename T> T min(T a, T b) { return (a < b) ? a : b; } template <> const char * min<const char *>(const char *s1, const char *s2); int main() { const char *first = min(“a”, “b”); } template <> const char * min<const char *>(const char *s1, const char *s2) { return strcmp(s1, s2) < 0 ? s1 : s2; } Must be seen by all translation units. Where do specializations go? In header files or in .cpp files? They go in .cpp files, because they are actually compiled.

501 Overloaded Templates Multiple templates can exist for a single function name. template <typename T> void func(Array<T>) {…} template <typename T> void func(List<T>) {…} List<int> l; Array<double> a; func(l); func(a);

502 There can be more than one match:
template <typename T> void func(T *p) { … } // V1 template <typename T> void func(T o) { … }; // V2 char *p; func(1); // Okay? func(p); // Okay? Which version? The most specialized version is chosen. Sometimes still ambiguous. What counts as more specialized? Accepts fewer matches. If something matches V1, will it match V2? What about vice versa? Which version accepts fewer matches? Must have same number of template parameters. func(1); // Chooses second version, since 1 is not a pointer to anything. func(p); // Chooses first version, since that version is more specialized.

503 Template Mixed with Nontemplate
Is this valid? template <typename T> T sum(T, int); template <typename T> T sum(T, double); double sum(double, double); Which one gets called? sum(3.14, 3.14); sum(3.14, 1); sum(1, 3.14); It is fine to mix templated functions with non-templated functions. The non-template function is preferred.

504 What is the advantage of a non-template over a specialization?
The advantage is that you skip the argument deduction phase, so you avoid some ambiguity traps. Suppose you have a special version of min() that you want to be called for all integer types, such as short, etc. // Generic definition. template <typename T> T min(T v1, T v2) { … } // Specialization template <> int min<int>(int v1, int v2) { … } int i = ...; short s = ...; min(i, s); // Which does this call? Can use non-template to enable more conversions. // Normal conversions will be used. inline int min(int v1, int v2) { return min<int>(v1, v2); } min(i, s); min(s, s); The special version of min() does not get called when a short is used, because the specialized version can only be selected AFTER template argument deduction. But template argument deduction does not use standard conversions like short to int.

505 Summary of Resolution For a given invocation: The steps are: min(…);
For each template, try to deduce arguments. If success, add the template to the overload list. Deduction failure by itself is not an error. Add all non-templated functions to the overload list. Use normal overload resolution. Prefer non-templated. If a specialization is selected, don’t instantiate.

506 Specialization Revisited
Recall: The default option should be to pass arguments by const reference. template <typename T> const T &min(const T &a, const T &b) { return (a < b) ? a : b; } We also need the specialization for strings: template <> const char * min(const char *s1, const char *s2) { return strcmp(s1, s2) < 0 ? s1 : s2; } Okay? Does not work, because the specialization doesn’t match any template.

507 Need same form. Okay: template <typename T> const T &min(const T &a, const T &b) { return (a < b) ? a : b; } template <> const char *const & min(const char *const &s1, const char *const &s2) { return strcmp(s1, s2) < 0 ? s1 : s2; } Okay now? Doesn’t work for string literals because they are array types.

508 Solution? const char * min(const char *, const char *);
template <typename T, size_t N1, size_t N2> const T * min(const T (&a1)[N1], const T (&a2)[N2]);

509 Class Templates

510 Class templates are “patterns” for classes. The syntax is:
Allow a class to be parameterized by type. The syntax is: template <typename T1, typename T2> class A …; T1 and T2 can be used anywhere.

511 Type parameter Int (non-type) parameter Example: template <typename T1, typename T2, int N> class A : public Base<T1, T2> { public: T2 member_func(T1); private: T1 data_member; T2 data_member; T1 a[N]; }; Type parameters can be used anywhere a type would normally be. A constant int parameter can replace any constant int. Type parameter used for derivation Non-type parameter use

512 Member functions are defined with:
template <typename T, int N> class A { … }; template <typename T, int N> void A<T, N>::func() { T obj; int i = N; }

513 What is the syntax to define an object using this template?
Another example: template <typename T, int N> class Array { T a[N]; T operator[](int i) { return a[i]; } }; What is the syntax to define an object using this template? Array<int, 10> a; How might you improve this? Add range check Allow the index operator to be used as an lvalue by returning a reference Adding a const version to be used with const arrays.

514 Template Parameters Can have multiple ones:
template <typename T1, class T2> class A; typename or class are the same. You can actually replace class with a fundamental type. template <class T> class B; B<int> a; // T is bound to int.

515 Inside a template, the name of the class refers to the parameterized name.
template <class T> struct Buffer { … Buffer *next; // Same as Buffer<T>. }; Is this okay? Buffer<int> b1; Buffer<double> b2; b1.next = &b2; // Okay?

516 Default arguments can be redefined.
Template parameters can have default arguments. (In C++11, also function templates.) template <typename T, typename Container_T = std::vector<T> > class Stack { private: Container_T elems; … }; Default arguments can be redefined. template <class Type, int size = 1024> class Buffer; Buffer<int> buf; // Size is template <class Type, int size = 2048> class Buffer; Buffer<double> buf2; // Size is 2048.

517 Instantiation A class template is used by giving the template name plus the parameters. template <typename T1, typename T2> class A { … }; … A<int, double> a; There is no template argument deduction for class templates. template <typename T> struct A { A(T); … }; A a(1234); // Error, does not create an A<int>.

518 The instantiation is this:
When you write: Stack<int> s; The instantiation is this: template <typename T> class Stack { private: std::vector<T> elems; public: void push(const T &); void pop(); T top() const; }; template <typename T> void Stack<T>::push(const T &o) { elems.push_back(o); } … class Stack<int> { private: std::vector<int> elems; public: void push(const int &); void pop(); int top() const; }; void Stack<int>::push( const int &o) { elems.push_back(o); }

519 Does this compile? Is it an instantiation?
An instantiated class template can be used anywhere that a non-template class can be used. If you can write MyClass, you can write MyTemplatedClass<int>. Instantiated only when used in a context that requires definition to exist. Does this compile? Is it an instantiation? template <typename T> class A; void foo(A<int> &); Not instantiation. How about this? void foo(A<int> &a) { a.foo(); } Yes, it is an instantiation. Different instantiations are completely independent.

520 Is this an instantiation?
int foo(Queue<int> *p) { } How about this? int foo(Queue<int> *p) { return p->doit(); } Not an instantiation until the compiler actually needs to know the class definition, so the first foo does not instantiate Queue<int>, but the second one does.

521 Member functions, recall, are defined with this syntax:
template <typename T> class A; template <typename T> void A<T>::foo() { … } Not instantiated till called or address taken. So, no need for definition unless called.

522 Example Example of a class template for a stack. template <typename T> class Stack { private: std::vector<T> elems; public: void push(const T &); void pop(); const T &top() const; }; template <typename T> void Stack<T>::push(const T &o) { elems.push_back(o); } template <typename T> void Stack<T>::pop() { elems.pop_back(); } template <typename T> T &Stack<T>::top() const { return elems.back(); }

523 A Template Idiom Is this okay? class A : public Tmpl<A> { … }; Does it cause infinite recursion? template <typename T> class Tmpl { T *ptr; }; template <typename T> class Tmpl { T t; }; [Show crtp/] Occurs often enough so that it is sometimes referred to as the curiously recurring template pattern (CRTP). This is okay, and does not cause infinite recursion, in general. If you use it wrongly, though, it can cause infinite recursion.

524 Function Templates Interacting with Class Templates
Consider: // Function 1 template <typename T> void foo(const T &) { ... } // Function 2 template <> void foo(const int &) { ... } // Function 3 void foo(const int &) { ... } What is Function 2? Function 3? How can I call Function 1? 2? 3? foo(3.14); foo<>(1); foo(1);

525 What is Function 2? Function 3? How can I call Function 1? 2? 3?
Consider: template <typename T> class A { ... }; // Function 1 template <typename T> void foo(const A<T> &) { ... } // Function 2 template <> void foo(const A<int> &) { ... } // Function 3 void foo(const A<int> &) { ... } What is Function 2? Function 3? How can I call Function 1? 2? 3? A<double> a_double; A<int> a_int; foo(a_double); foo<>(a_int); foo(a_int);

526 Friends A friend can be defined in a number of ways.
class A { template <typename T> friend void f(); friend class B<int>; … }; template <typename T> class B { friend class C<T>; template <typename> friend class D; … };

527 No. A function template is not the same as a non-templated function.
Does this work? template <typename T> class A; template <typename T> int foo(A<T> &o) { return o.private_member; } template <typename T> class A { friend int foo(A<T> &); int private_member; }; No. A function template is not the same as a non-templated function. template <typename T> int foo(T &o) { return o.private_member; } class A { friend int foo<>(A &); int private_member; };

528 Static Data Members A template class can have static data members.
template <typename T> class A { static T static_member; }; template <typename T> T A<T>::static_member;

529 Nested Types of Class Templates
A class template can have a nested class. template <typename T> class A { class Buffer { T *data; }; }; Can only be accessed with instantiation. A::Buffer; // Error. A<int>::Buffer; // Okay.

530 typename keyword Consider What if T is? Use typename keyword
template <typename T> void foo(const T &) { T::name *p; } Is this a syntax error? What if T is? struct A1 { static int name; }; foo(A1()); struct A2 { typedef int name; }; foo(A2()); Can’t tell without knowing details of T. Use typename keyword template <typename T> void foo(T) { typename T::name *p; } Compiler doesn’t know whether or not to flag this as a compiler error.

531 Specialization As with function templates, class templates can be specialized for a particular set of template arguments. template <> class A<int, double> { … };

532 Specialization of Stack class:
template <> class Stack<std::string> { private: std::deque<std::string> elems; public: void push(const std::string &); void pop(); const std::string &top() const; }; void Stack<std::string>::push(const std::string &o) { elems.push_back(o); } void Stack<std::string>::pop() { elems.pop_back(); } std::string &Stack<std::string>::top() const { return elems.back(); }

533 The specialization can be completely different.
template <typename T> class Stack { private: std::vector<T> elems; public: void push(const T &); void pop(); T top() const; }; // Specialization template <> class Stack<int> { private: char a[10]; public: double foogoo(); };

534 Nothing is carried over from the generic implementation.
template <typename T> struct A { void generic_foo(T *) { … } … }; template <> struct A<int> { … }; … A <int> a; // Implemented in generic template, okay? a.generic_foo(); Why not? What if generic_foo() uses member variables? Are they present in the specialized class template? Nothing is carried over.

535 You can also specialize member functions individually:
template <typename T> class A { T foo() { … } // Generic impl. }; // Specialization. template <> double A<double>::foo() {…} Where do these go, header or program text? Go in .cpp file, because they are not templates. Must place a declaration before point of instantiation. Must go in program text. Specialization must have the same signature.

536 Partial Specialization
In a full specialization, all template parameters are filled in. In a partial specialization, some template parameters are filled in. template <typename T1, typename T2> class MyClass {…}; template <typename T> class MyClass<T, int> {…};

537 Or, the template parameters can just be more specific.
template <typename T1, typename T2> // 1 class MyClass {…}; template <typename T> // 2 class MyClass<T, int> {…}; template <typename T1, typename T2> // 3 class Myclass<T1*, T2*> {…}; MyClass<int, double> obj1; MyClass<char *, int> obj2; MyClass<int *, double *> obj3; MyClass<int *, double> obj4; obj1 uses no specialization. obj2 uses first specialization. obj3 uses second specialization.

538 DOES NOT MEAN: Some aspects specialized, others left as is.
Further examples: template <typename T, int N> class A { … }; template <typename T> class A<T, 2> { … }; A bit poorly named: Full specialization: All parameters filled in. Partial specialization: Some parameters filled in, at least one left free. DOES NOT MEAN: Some aspects specialized, others left as is. As with full specialization, nothing is carried over from the unspecialized template.

539 Partial Specialization vs. Overloaded Function Templates
Function templates cannot be partially specialized, but can be overloaded. Class templates cannot be overloaded, but can be partially specialized.

540 Usually, the distinction is not crucial:
template <typename T1, typename T2> void foo(T1, T2); template <typename T1> void foo<T1, int>(T1, int); // No. template <typename T1> void foo(T1, int); // Okay. template <typename T1, typename T2> void foo(T1 *, T2); // Okay.

541 template <typename T1, typename T2> class A {
template <typename T1, typename T2> class A { ... }; template <typename T1> class A { ... }; // No template <typename T1> class A<T1, int> { ... }; // Okay. template <typename T1> class A<T1 *, int> { ... }; // Okay.

542 member templates Hide temporarily, for lecture.

543 Member Templates Consider: Prints out ID of output stream before object. class Output { public: void print(int v) { os << id << “: “ << v << endl; } void print(double v) { os << id << “: “ << v << endl; } void print(const A &obj) { os << id << “: “ << obj << endl; } … }; What if we add a new class named B? Is there any way we can avoid duplicating all this code? The solution to avoid so much duplication is to use a member template.

544 Member templates can also be classes:
We can use a member template. A member template is a template for a single member of a class. class Output { public: template <typename T> void print(const T &v) { os << id << “: “ << v << endl; } … }; Member templates can also be classes: class Output { template <typename T> class Helper { … }; … };

545 Three possibilities: class Output { template <typename T> class Helper { … }; … }; template <typename T> class Output { class Helper { … }; … }; template <typename T> class Output { template <typename U> class Helper { … }; … }; In the first, Output is not a class template. Only Helper is a class template. In the second, both are class templates, but there relationship is fixed. In the bottom, both are class templates also, but the relationship is less fixed.

546 To define a member template outside of a class, use this syntax:
class Output { public: template <typename T> class Helper; template <typename T> void print(const T &v); … }; template <typename T> class Output::Helper { … }; template <typename T> void Output::print(const T &v) { os << id << “: “ << v << endl; }

547 If the outer class is also a template, the syntax for defining outside of the class is:
template <typename T> class A { public: template <typename U> class Helper; template <typename U> void foo(const U &); … }; template <typename T> template <typename U> class A<T>::Helper { … }; template <typename T> template <typename U> void A<T>::foo(const U &u) { … }

548 Operators can also be member templates:
class A { template <typename T> void operator+(const T &); }; A a; a + 2; // Calls template.

549 Special Member Templates
Let’s say you have an array type, named Array. Since it’s a container, you make it templated. template <typename T> class Array { … }; What is the relationship between Array<int> and Array<short>? Array<int> int_array; Array<short> short_array; … int_array = short_array; // Okay? // Okay? Array<int> int_array2(short_array); C++ treats the two arrays as completely different types, so nothing will work.

550 What is the situation between Array<A> and Array<B>?
Let’s say that someone else is using your Array class. She has a class A and B, and has provided conversion and assignment operators in both directions A  B. A a(…); B b(…); A a1(b); B b1(a); a1 = b1; b = a; What is the situation between Array<A> and Array<B>? Array<A> a_array; Array<B> b_array; … a_array = b_array; // Okay? Array<A> a_array2(b_array); // Okay? C++ treats them as completely different types, so nothing will work.

551 Solution is templated constructors and/or assignment operators.
template <typename T> class Array { public: template <typename U> Array(const Array<U> &a) { // Do allocation, etc for (int i = 0; i < N; i++) { this->data[i] = a.data[i]; } } }; Let’s say there is no conversion between the types C and D. Array<C> c_array; … Array<D> d_array(c_array); // Okay? Where in the source code above does the error occur? The error occurs in the assignment in the template.

552 What constructor is used to construct a2?
Consider: template <typename T> class A { public: A(int); template <typename U> A(const A<U> &o); private: }; What constructor is used to construct a2? A<int> a1(2134); A<double> a2(a1); A<double> a3(a2); Template copy constructors do not inhibit generation of implicit copy constructor. Even deleting them doesn’t help.

553 Conversion operators can also be templates:
class A { public: template <typename T> operator T(); }; struct B { }; struct C { C(int); }; A a; (int) a; // Uses the conversion operator. int i = a; // Okay? B b(a); // Okay? C c(a); // Okay? Okay, except for the last one, because A object could be converted to int or to C.

554 Member templates can have explicit arguments:
class A { public: template <typename T> void foo(); template <typename T> A(); }; A a; a.foo<int>(); // Okay? A a2<double>; // Okay? A a2<double>; does not compile. There is no way to invoke a templated default constructor.

555 One solution: class A { public: template <typename T> A(T *); };
How do you invoke the constructor? A a((T *) 0); Disadvantages? Might conflict with an actual constructor that takes pointers.

556 A better solution: class A { public: template <typename T> A(tag<T>); }; How do you invoke the constructor? A a(tag<int>()); How is tag defined? template <typename T> struct tag {};

557 Virtual Member Templates
Can member templates be virtual? class A { public: template <typename T> virtual void foo(); … }; class B : public A { public: template <typename T> virtual void foo(); … }; Member templates cannot be virtual. The problem is instantiation. Instantiation happens when a function is called. The compiler figures out what template arguments should be, then instantiates. But if the call is virtual, the compiler cannot figure out at compile time what virtual functions are being called. In other words, the vtable is filled in when an object is created. But if the object has a member template, it is not known at compile time when the object is created what member function templates need to be instantiated.

558 No, because instantiation is problematic.
class A { public: template <typename T> virtual void foo(); … }; class B : public A { public: template <typename T> virtual void foo(); … }; void foo(A &a) { a.foo<C>(); // What is a? } Member templates cannot be virtual. The problem is instantiation. Instantiation happens when a function is called. The compiler figures out what template arguments should be, then instantiates. But if the call is virtual, the compiler cannot figure out at compile time what virtual functions are being called. In other words, the vtable is filled in when an object is created. But if the object has a member template, it is not known at compile time when the object is created what member function templates need to be instantiated.

559 Summary Member function templates: Member class templates:
They behave much like normal function templates. Template argument deduction? Specialization? Member class templates: They behave much like normal class templates. Can have specializations and partial specializations.

560 Advanced Topics

561 Parsing: 1: int x=2; x * y; // What does this mean? 2: typedef int x;
X<1>(0); // What does this mean? 1: int x=2; 2: typedef int x; 1: int X=2; 2: template <int N> struct X { X(int); };

562 Template Template Parameters
Let’s say that we want to make a stack container. It may contain different type of objects. So we make it a template. template <typename T> void Stack<T>::push(const T &o) { elems.push_back(o); } template <typename T> void Stack<T>::pop() { elems.pop_back(); } template <typename T> T Stack<T>::top() const { return elems.back(); } template <typename T> class Stack { private: std::vector<T> elems; public: void push(const T &); void pop(); T top() const; };

563 template <typename T> void Stack<T>::push(const T &o) { elems.push_back(o); } template <typename T> void Stack<T>::pop() { elems.pop_back(); } template <typename T> T Stack<T>::top() const { return elems.back(); } template <typename T> class Stack { private: std::vector<T> elems; public: void push(const T &); void pop(); T top() const; }; This version is based on a vector. Suppose sometimes we want to use a vector, but other times we want to use a list. Or maybe we want to let the user plug-in her own underlying container type. How to handle? Standard approach is to use polymorphism. (A member function template can’t be virtual, but a class template can have virtual functions.)

564 Possible disadvantages of this method? Can I use it with std::vector?
template <typename T> void Stack<T>::push(const T &o) { elems->push_back(o); } template <typename T> void Stack<T>::pop() { elems->pop_back(); } template <typename T> T Stack<T>::top() { return elems->back(); } template <typename T> class Stack { private: Container<T> *elems; public: void push(const T &); void pop(); T top() const; }; User does: Stack<int> stack(new MyContainer<int>); Possible disadvantages of this method? Can I use it with std::vector? Intrusive (requires modifying the container templates), possibly not so efficient, prevents use of member templates.

565 Use another template parameter.
template <typename T, typename C> class Stack { private: C elems; public: void push(const T &); void pop(); T top() const; }; template <typename T, template C> void Stack<T, C>::push(const T &o) { elems.push_back(o); } … Stack<int, std::vector<int> > stack; Not that bad in this simple example, but you might want to use a vector of ints and a vector of int * in a single class, for example.

566 No way to use this to create two different containers with different type parameters.
template <typename T, typename C> class SomeClass { private: C something; // Contains ints. C something_else; // Contains int *. }; SomeClass<int, std::vector<int> > obj;

567 Instead, can use something known as a template template parameter.
A normal template parameter says: I don’t know what the type of this is yet. A template template parameter says: I don’t know what the type of this is yet, but it will be a (uninstantiated) template. template <typename T, template <typename> class CONT> class Stack { private: CONT<T> elems; … }; template <typename T, template <typename> class CONT> void Stack<T,CONT>::push(const T &o) { elems.push_back(o); } … Stack<int, std::vector> s; // Not Stack<int, std::vector<int> >

568 Does this compile? template <typename T1, typename T2> class A { … }; template <typename T, template <typename> class CT> class B { … }; … B<int, A> b; No. Because the class template A has two template parameters, but the template template parameter in B has only one template parameter.

569 If you try to use this with the standard C++ containers, it will fail.
template <typename T, template <typename> class CONT> class Stack { private: CONT<T> elems; public: void push(const T &); void pop(); T top() const; }; … Stack<int, std::vector> stack; // Syntax error! The std::vector class has 2 template parameters, so there will be a mismatch. In other words, a template template parameter has to have the same number and type of whatever you pass in as an actual argument. Why? Hint: How many template parameters does std::vector have?

570 Solution is to make the template template parameter’s template parameters match the template parameters of the C++ standard container classes. template <typename T, template <typename E, typename A = std::allocator<E> > class CT> class Stack { private: CT<T> elems; public: void push(const T &); void pop(); T top() const; }; … Stack<int, std::vector> stack; // Okay now. Now, the number of template parameters that std::vector has is the same as the number of template parameters that CT has.

571 Can also be used with function templates.
template <typename T, template <typename E, typename A = std::allocator<E> > class C> void foo() { C<T> container; } int main() { foo<int, std::vector>(); }

572 More On Non-Type Template Parameters
The main requirement is that it be constant. You can do things like this: template <typename T, int VAL> T addValue(const T &x) { return x + VAL; } You cannot use doubles, or string literals: template <double VAL> double func(double x) { … } // Syntax error. template <const char *name > class MyClass { … }; MyClass<“hello”> obj; // Error. extern const char s[] = “hello”; MyClass<s> x; // Okay. Why are string literals not allowed? There is no guarantee that two string literals that are the same will have the same address.

573 What does this do? template <typename T, int (T::*mptr)> class Incrementor { public: Incrementor(T *p) : obj(p) {} void inc() { (obj->*mptr)++; } private: T *obj; }; struct A { int value1; int value2; } a; Incrementor<A, &A::value1> inc1(&a); Incrementor<A, &A::value2> inc2(&a); inc1.inc(); inc2.inc(); inc2.inc(); This uses a pointer-to-member as a non-type template parameter to create a functor-like object that is bound (at compile-time) to a particular member of a class. Calling inc() on the object then increments the bound variable. You can also do this at run-time, but it is not as efficient.

574 Zero Initialization One of the issues with templates is the distinction between “fundamental” values, like int, double, etc. and “objects”. The same code should work for both. In Java, one way around this the Integer class. In C++, this mainly shows up in initialization issues. template <typename T> void foo() { T x; // Is this initialized? } Depends on whether it is a fundamental type or not. Whether or not x is initialized depends on whether it is a fundamental type or a class.

575 Solved by defining int() to be zero.
template <typename T> void foo() { T x = T(); } template <typename T> class A { public: A(); private: T x; … }; template <typename T> A<T>::A() : x() {…}

576 .template In some situations, there is a similar type of ambiguity to the typename issue. Is this a syntax error? template <int N> void print(const bitgroup<N> &bs) { std::cout << bs.to_string<char>(); } Disambiguate with template keyword. template <int N> void print(const bitgroup<N> &bs) { std::cout << bs.template to_string<char>(); }

577 String Literals in Template Argument Deduction
Consider: template <typename T> const T &max(const T &a, const T &b) { … } … max(“a”, “b”); // Okay. max(“aa”, “bb”); // Okay. max(“a”, “bb”); // Is this okay? The issue is that the type of a string literal like “a” is const char[2]. If you use a non-reference parameter, on the other hand, the array-to-pointer decay will occur. template <typename T> T max(T a, T b) { … } … max(“a”, “b”); // Okay. max(“a”, “bb”); // Okay. You can kind of solve this with an overloaded template, but it doesn’t help much anyway, due to the return type. template <typename T, size_t N1, size_t N2> ??? max(const T (&a1)[N1], const T (&a2)[N2]) { ... }

578 Argument Dependent Lookup (Koenig)
Remember that operators for objects are just a kind of “syntactic sugar” for functions: if (a < b) {…} is exactly the same as if (operator<(a,b)) { … } This is a function call!

579 Should there be namespaces somewhere here?
Consider this: // Library A code. class BigInt {…}; // Assume operator = defined. bool operator<(const BigInt &, const BigInt &); // Library B code. template <typename T> inline const T &max(const T &a, const T &b) { return a < b ? a : b; } // User code. void foo(const BigInt &bi1, const BigInt &bi2) { BigInt x = max(b1, b2); // Okay? } Should there be namespaces somewhere here?

580 So being good C++ citizens, we add namespaces:
// Library A code. namespace cs540 { class BigInt {…}; // Assume operator = defined. bool operator<(const BigInt &, const BigInt &); } // Library B code. namespace std { template <typename T> inline const T &max(const T &a, const T &b) { return a < b ? a : b; } } // User code. void foo(const cs540::BigInt &bi1, const cs540::BigInt &bi2) { cs540::BigInt x = std::max(b1, b2); // Okay? } Does this still work?

581 The < operator is in a different namespace from the template.
But ADL says that names should be looked up in scopes associated with the arguments. Dependent qualified names are looked up. ADL is done for unqualified names. Using directives are ignored during ADL, but using declarations are obeyed. ADL is inhibited if the function name is enclosed in parentheses: (f)(x); // No ADL. Also, using directives are ignored: namespace ns1 { void foo(int); } namespace ns2 { using namespace ns1; class A {}; void foo(const A &); } int main() { foo(A()); // Does not search ns1.

582 Consider this: namespace cs540 { class A { … }; void foo(const A &a); std::ostream & operator<<(std::ostream &os, const A &); } int main() { cs540::A a; cs540::foo(a); // Okay? foo(a); // Also, okay? operator<<(std::cout, a); // Okay? std::cout << a << std::endl; // Okay? } cs540::foo(a); // Is okay, since it uses namespace. foo(a); // Is okay, since ADL kicks in.

583 Consider this: namespace cs540 { class A { … }; void foo(const A &a) { … } } int main() { cs540::A a; cs540::foo(a); // Okay? foo(a); // Okay? } // Which one does it call? void foo(const cs540::A &a) { … } // Which one does it call? void foo(const cs540::A &a) { … } In the above case, the one in the namespace will be preferred.

584 There are two ways to parse the above:
Consider this: namespace ns { class X { }; template <int I> void select(X*); } void g(ns::X *xp) { select<3>(xp); // Does ADL work here? } There are two ways to parse the above: Function name Arg select<3>(xp) Comparison Comparison select<3>(xp) The question is whether or not xp is a function call argument. If yes, then ADL will apply. If not, then ADL does not apply. But whether or not it is a function call argument depends on whether or not <3> is a template argument....but this depends on whether or not xp is a function call argument...but this depends on whether or not <3> is a template argument.... Since there is a chicken and egg problem, this doesn’t work.

585 Explicit argument  No ADL.
Can still trigger it though by having a function template declaration visible. Doesn’t matter what. Forces compiler to parse as function template. namespace ns { class X { }; template <int I> void select(X*); } template <int> void select(); void g(ns::X *xp) { select<3>(xp); // ADL works now. }

586 Friend Name Injection Consider this:
template <typename T> class A { friend void f(); friend void f(const A<T> &); }; void g(A<int> *p) { f(); // Okay? f(*p); // Okay? } Upshot is that friend functions are found if they are friends of classes that are part of the ADL.

587 Class Name Injection Inside a template, the class name can be used alone: template <typename T> class C { C foo(); C<double> goo(); }; In this case, it is considered to be shorthand for C<T>, which is not a template. This means that o.foo() returns an object of type C<int> if o is of type C<int>, etc.

588 template <template <typename> class TT> class X { }; template <typename T> class C { C *a; C<void> b; X<C> c; X<::C> d; }; Need to have a space: X< ::C> e;

589 Dependent and Nondependent Names
Within a template, there are two types of names. dependent: The exact meaning depends on a template parameter. nondependent: The exact meaning does not depend on a template parameter. Which are dependent/nondependent? template <typename Type> Type min(Type *array, int size) { Type min_val = array[0]; for (int i = 1; i < size; i++) { if (array[i] < min_val) { min_val = array[i]; } } print("Min found: "); print(min_val); return min_val; }

590 Consider two translation units containing the below:
First translation unit: template <typename T> void foo(T v) { func(v); // func() is not declared yet. // print() is not declared yet. print(“hello”); } Second translation unit: void func(int); void print(const char *); foo(1); Does this compile? It does not compile, because the print(“hello”) is resolved at the point that it is called.

591 Name Resolution Name lookup occurs in two phases.
In the first phase, nondependent names are resolved where they are used. Thus, all names not dependent on a template must be declared at point of template definition. In the second phase, dependent names are resolved at the point of instantiation. Thus, all names dependent on a template not resolved till point of instantiation.

592 Point of Instantiation (POI)
The point of instantiation (POI) of a function template is always in namespace scope, and occurs immediately after the function that instantiated it. int main() { … min(…); // Use of a function template min(). } // <<<< POI of the use above is here. What if the same function is instantiated multiple times? Compiler can pick any POI. What if in more than one file? Compiler can pick any one.

593 Why is the POI after the use of a function template, rather than before?
Consider: template <typename T> void f(T o) { if (o > 0) { g(o); // ? } } … // Possible POI 1 void g(FooType ft) { f<FooType>(ft); } // Possible POI 2 The POI is after the use to allow for recursive calls.

594 Where should the POI of a class template be?
template <typename T> class S { public: T m; }; // Possible POI 1 unsigned long h() { return (unsigned long) sizeof(S<int>); } // Possible POI 2 The point of instantiation (POI) of a class template is always in namespace scope, and immediately precedes the declaration or definition that uses the instantiation. The POI of a member function or static data member immediately follows it's use.

595 Template Aliases A typedef can only be made to a concrete type.
Recall our template template parameter example: template <typename T> class MySimpleContainer { … }; template <typename T, template <typename> class C> class A { C<T> container; }; int main() { A<int, MySimpleContainer> a1; A<int, std::vector> a; } What’s the problem?

596 You can solve this by creating a template alias:
template <typename T> class MySimpleContainer { … }; template <typename T, template <typename> class C> class A { C<T> container; }; template <typename T> using myvector = std::vector<T, std::allocator<T> >; int main() { A<int, MySimpleContainer> a1; A<int, myvector> a; }

597 std::map takes two template arguments, the key and value.
map<string, A *> Let’s say that you were tired of typing the first template argument. You can use a template alias: template <typename T> using mymap = map<string, T>; mymap<int> m;

598 NDC

599 Repetitive Code Suppose you are writing some code, and you see that the following lines repeated 4 times in your code within a single function: Local variables are in blue, code that is not the same in all four occurrences is in red. … Element *e = new Element(bindings_table.lookup(String()), String(st_name_bc_begin, p)); if (root != 0) { element_stack.top().element->add_child(e); } else { root = e; } element_stack.push(e, String(st_name_bc_begin, p), binding_list_head); …

600 … Element. e = new Element(bindings_table
… Element *e = new Element(bindings_table.lookup(elem_prefix), String(st_name_ac_begin, p)); if (root != 0) { element_stack.top().element->add_child(e); } else { root = e; } element_stack.push(e, String(st_name_bc_begin, p), binding_list_head); … Element *e = new Element(bindings_table.lookup(elem_prefix), name); if (root != 0) { element_stack.top().element->add_child(e); } else { root = e; } element_stack.push(e, check_name, binding_list_head); …

601 Pattern is: So what? Solution?
… Element *e = new Element(bindings_table.lookup(elem_prefix), name); if (root != 0) { element_stack.top().element->add_child(e); } else { root = e; } element_stack.push(e, check_name, binding_list_head); … Pattern is: … Element *e = new Element(bindings_table.lookup(str1), str2); if (root != 0) { element_stack.top().element->add_child(e); } else { root = e; } element_stack.push(e, str3, binding_list_head); … So what? Solution?

602 You could use a function:
void create_element( BindingTable *bt, Element **root, ElementStack *es, const BindingTable::Binding *blh, const String &pref, const String &nm, const String &cn) { Element *e = new Element(bt->lookup(pref), nm); if (*root != 0) { es->top().element->add_child(e); } else { *root = e; } es->push(e, cn, blh); }

603 Which would then be called with:
create_element(&bindings_table, &root, &element_stack, binding_list_head, String(), String(st_name_bc_begin, p), String(st_name_bc_begin, p)); create_element(&bindings_table, &root, &element_stack, binding_list_head, elem_prefix, String(st_name_ac_begin, p), String(st_name_bc_begin, p)); create_element(&bindings_table, &root, &element_stack, binding_list_head, elem_prefix, name, check_name); create_element(&bindings_table, &root, &element_stack, binding_list_head, elem_prefix, name, check_name); Other options?

604 You could use a local class to encapsulate a function.
struct Locals { void create_element(const String &pref, const String &nm, const String &cn) { Element *e = new Element( bindings_table->lookup(pref), nm); if (root != 0) { element_stack.top().element->add_child(e); } else { root = e; } element_stack.push(e, cn, binding_list_head); } BindingTable bindings_table; ElementStack element_stack; const BindingTable::Binding *binding_list_head; Element *root; } l;

605 It would be used like this:
l.create_element(String(), String(st_name_bc_begin, p), String(st_name_bc_begin, p)); l.create_element(elem_prefix, String(st_name_ac_begin, p), String(st_name_bc_begin, p)); l.create_element(elem_prefix, name, check_name); l.create_element(elem_prefix, name, check_name); … l.bindings_table.accessor() …;

606 Note that you are using the local class to create a local function that “captures” some local variables. You can do that explicitly using lambda in C++11.

607 Lambda Lambda allows you to create anonymous functions. Example:
cout << [](int x){return x + 3;}(4) << endl; auto doit = [] (int x) { return x + 2; } cout << doit(1) << endl; In the body, you can only use captured variables: int i, j; auto doit = [i] () { cout << i << endl; // Okay. cout << j << endl; // Syntax error. } You can capture by reference, also: int i = 1, j = 2; auto doit = [&i, j]() { i = 3; // Okay. }

608 Example: A common task is to apply a small bit of code to all the elements in some container.
#include <iostream> #include <array> #include <algorithm> using namespace std; void f(int i) { cout << i << endl; } int main() { array<int, 5> a{{1, 2, 3, 4, 5}}; for_each(a.begin(), a.end(), f); }

609 What if we want to collect all odd elements?
#include <iostream> #include <array> #include <algorithm> #include <vector> using namespace std; class CollectOdd { public: Odd(vector<int> *v) : vec(v) {} void operator()(int i) { if (i%2 == 1) { vec->push_back(i); } } private: vector<int> *vec; }; int main() { array<int, 5> a{{1, 2, , 4, 5}}; vector<int> result; CollectOdd f(&result); for_each(a.begin(), a.end(), f); for (auto i : result) { cout << i << endl; } }

610 Can be done with lambda. #include <iostream> #include <array> #include <algorithm> using namespace std; int main() { array<int, 5> a{{1, 2, 3, 4, 5}}; vector<int> result; for_each(a.begin(), a.end(), [&](int i) { if (i%2 == 1) { result.push_back(i); } } ); }

611 Lambdas are actually anonymous class objects, generated by the compiler.
int i = 1, j = 2; auto doit = [&i, j](int k) { i = j*k; return i + j*k; }; doit(123); int i = 1, j = 2; class Anon { Anon(int &i, int j) : i(i), j(j) {} int operator()(int k) const { i = j*k; return i + j*k; } } doit(i, j); doit(123);

612 You can capture implicitly (default):
int i = 1, j = 2; auto doit = [&, i]() { j = 1; // Implicit capture by reference. i = 3; // Error, captured by value. } int i = 1, j = 2; auto doit = [=, &j]() { i = 2; // Error, implicit capture by value. j = 3; // Okay. } You change variables captured by value by creating mutable lambdas, but it won’t change the outer: int i = 1, j = 2; auto doit = [&, i]() mutable { j = 1; // Implicit capture by reference. i = 3; // Okay, doesn’t change outer. }

613 Variables captured by value are data members.
int n = 0; auto l = [n]() mutable { std::cout << n << std::endl; n++; }; l(); l(); l();

614 The return type can be inferred if just one return statement or void.
auto l1 = [] () { return 1234; }; auto l2 = [](int n) { for (int i = 0; i < n; i++) { cout << i << endl; } }; Otherwise, must use a trailing return type. auto l3 = [](int n) mutable -> int { if (n%2 == 0) { return 1; } else { return ‘a’; } }; In C++14, return type deduction works even if not just a single return statement, and also works for non-lambda.

615 Lambdas can be put into variables:
auto f = [](int i) { return 2*i; }; auto j = f(2); To pass a lambda into a non-template function, use std::function: void f(const std::function<int (int)> &f) { int i = f(2); … } f([](int i) { return 2*i; });

616 In C++14, you can have generic lambdas (more or less like a templated lambda). Handy in two ways:
// C++11 auto sqr1 = [](int x) {return x*x; }; double x = sqr1(3.14); // C++14 auto sqr2 = [](auto x) { return x*x; }; int i = sqr2(9); // Does the right thing. double x2 = sqr2(3.14); // Does the right thing. // Also allows compiler to figure out // types for you. std::sort(cont.begin(), cont.end(), [](auto i, auto j) { return (i > j); });

617 Also, in C++14, you can have generalized capture:
int i; auto l = [&v1 = i, v2 = 2*i, v3 = 1]() mutable { v1 = 3; // Sets i. cout << v3++ << endl; }; l(); l();

618 Stop here, Spring 2018

619 Using function local static for init order issues

620 Forward and Backwards Compatibility
What do they mean? Suppose we are writing a web browser. Should it be able to read in old versions of HTML? How to ensure? Suppose you are writing a web browser. Should the browser be able to handle future versions of HTML? How do we ensure this? Forward compatibility: The ability of an application to accept future versions of input. Backward compatibility: The ability of an application to accept previous versions of input. Which is harder? Ensuring forward compatibility requires being able to predict the future, so is not 100% possible.

621 ABI (Application Binary Interface) vs. API
Consider a module within an application. You design the module interfaces carefully. Version 2, implementation changes, but not the interfaces. Does the rest of the application need to be modified? Does it need to be recompiled? Why does this matter?

622 Original: Modified: Client code: Recompile?
class A { public: void foo(); private: int i; }; Modified: class A { public: void foo(); private: int i, j; }; Client code: A a; Recompile?

623 Original: Modified: Client code: Recompile?
class A { public: void foo(); private: int i; }; Modified: class A { public: void foo(); void foo2(); private: int i; }; Client code: A a; a.foo(); Recompile?

624 Original: Modified: Client code: Recompile?
class A { public: virtual void foo(); private: int i; }; Modified: class A { public: virtual void foo(); virtual void foo2(); private: int i; }; Client code: A a; a.foo(); Recompile?

625 Original: Modified: Client code: Recompile?
void A::foo() { printf(“%d\n”, 1234); } Modified: void A::foo() { printf(“%d\n”, 5678); } Client code: A a; a.foo(); Recompile?

626 × × In general: However...more specifically:
Header file change  recompile Implementation file change  recompile Basically, this is what the standard states. However...more specifically: Existing machine code breaks  recompile. Existing machine code doesn’t break  recompile. Platform dependent, NO GUARANTEES. × ×

627 [See /usr/include/gtk-2.0/gtk/gtkwindow.h]
Is Node v2 backwards binary compatible with v1? struct Node { char version; double x; int i; int p1, p2, p3; // Padding for extensibility. }; struct Node { // v2 char version; double x; int i; double y; }; [See /usr/include/gtk-2.0/gtk/gtkwindow.h] Node v2 is probably backwards binary compatible. A definitive answer requires detailed knowledge about the platform.

628 Can also be achieved with wrappers (PImpl idiom).
// A.hpp class Aimpl; class A { public: void method1(); private: Aimpl *impl; }; // A.cpp void A::method1() { impl->method1(); } // Aimpl.hpp class Aimpl { … }; What does a client need to include? #include “A.hpp” #include “Aimpl.hpp” // Needed? Modify A.cpp: Recompile rest of program? Modify Aimpl.hpp: Recompile A.cpp? Recompile rest of the program? Client does not need to include Aimpl.hpp. Changing A.cpp does not require a recompile of the rest of the program. Changing Aimpl.hpp also does not require a recompile of the rest of the program, but does require a recompile of A.cpp.

629 Binary compatibility is important in these situations:
Large application with plug-ins/modules/extensions: Want to ship bug fixes/new versions to customers. With no binary compatibility, what happens? With binary compatibility? Designing an OS: When you upgrade the OS, what happens to apps? Libs and dlls: What happens if there is a bug in the C library on Windows or Linux? Binary compatibility is guaranteed by adhering to an ABI (Application Binary Interface). With no binary compatibility, any changes requires changing everything. If there is a bug in the C library, you want to be able to fix it without making all apps incompatible. So it’s important to maintain binary compatibility in such a case.

630 What about linking? Not an issue as long as run-time linking is used.


Download ppt "Advanced OOP CS 440/540 Spring 2019 Kenneth Chiu"

Similar presentations


Ads by Google