C++: Unexpected Behaviour

Antonio Mallia

Jaime Alonso

About Us

Antonio Mallia

Jaime Alonso

Software Engineers @ Bloomberg

Bloomberg

Bloomberg is just finance, right?

  • A technology company with 5000+ engineers
  • Not only C++, also JS, Python, Go, Java, and Haskell
  • Increasing use and contribution to open source

Motivation

Definitions

UB != Unexpected Behaviour

Let's abbreviate Unexpected Behaviour as UXB

UXB occurs when a program does not perform what the programmer initially intended and instead causes an undesired result.

Arrays


                    int a[10];
                    ...
                    a[5] = 1;

                    5[a] = 1;
                    
The C standard defines the [] operator as follows:

                    a[5] == *(a + 5)
                    
Therefore 5[a] will evaluate to:
*(5 + a) == *(a + 5)

Encapsulation

“ Encapsulation is used to hide the values or state of a structured data object inside a class, preventing unauthorized parties' direct access to them.”
Wikipedia

                        class Account {
                        public:
                            virtual void deposit(int amount) {
                                accountBalance += amount;
                            }
                            virtual void balance() {
                                std::cout << "Balance: " << accountBalance << std::endl;
                            }
                        protected:
                            int accountBalance = 0;
                        };
                        
                        class EmployeeAccount : public Account {
                        private:
                            virtual void paySalary() { accountBalance += 1000; }
                        };
                        

                        int main()
                            Account a;
                            a.deposit(100);
                            a.balance(); // 100

                            EmployeeAccount e;
                            e.paySalary(); // won't compile
                        }
                        

                        class Account {
                        public:
                            virtual void deposit(int amount) {
                                accountBalance += amount;
                            }
                            virtual void balance() {
                                std::cout << "Balance: " << accountBalance << std::endl;
                            }
                            virtual void paySalary() {}
                        protected:
                            int accountBalance = 0;
                        };
                        class EmployeeAccount : public Account {
                        private:
                            virtual void paySalary() { accountBalance += 1000; }
                        };
                        

                        int main()
                            Account a;
                            a.deposit(100);
                            a.balance(); // 100

                            EmployeeAccount e;
                            
                            e.paySalary(); // won't compile
                            
                            EmployeeAccount e;
                            Account* ea = &e;
                            ea->paySalary();
                            a.balance(); // 1000
                        }
                        

                            while(true){
                                ea->paySalary();
                            }
                        

The long arrow operator


                            size_t x = 10;
                            while(0<--x);
                            std::cout << x << std::endl;  // 0 
                        

--> is not an operator

It is equivalent to:

                            while(0 < (--x));
                            

What if we make it longer?


                            wp----->size();
                        

The long arrow is not a single operator, but a combination of multiple operators.

Here it is a normal -> operator and the postfix decrement operator --

                            ((wp--)--)->length();
                        

How can we overload them?

Implicit conversions

It can be a double-edged sword

                        void greeting(std::string str) {
                            std::cout << str << std::endl;
                        }
                        
                        void greeting(bool german) {
                            if(german)
                                std::cout << "Hallo Welt!" << std::endl;
                            else
                                std::cout << "Hello World!" << std::endl;
                        }
                        
                        
                        greeting("Ciao mondo!"); // Ciao mondo!
                        
                        
                        greeting("Ciao mondo!"); // Hallo Welt!
                        
                        

Overload resolution

Type deduction can help us

                        struct wrapper
                        {
                            template<typename T>
                            bool operator()(T&& a)
                            {
                                return isOne(a);
                            }
                        };

                        std::find_if(my_vec.begin(), my_vec.end(), wrapper());
                        
                        std::find_if(my_vec.begin(), my_vec.end(),
                                     [](auto&& a){ return isOne(a); });
                        
                        

Overload resolution II

Ternary operator

Optimizations and UXB

Optimizations: code elimination

Optimizations: simplifications

Optimizations: static initialization

Copy elision

Conclusion

Where to find these slides

http://cpp-unexpected-behaviour.github.io/topconf2017

Thank you!