CppCon 2014: Herb Sutter "Back to the Basics! Essentials of Modern C++ Style"

By: CppCon

965   22   135162

Uploaded on 09/30/2014

http://www.cppcon.org
--
Presentation Slides, PDFs, Source Code and other presenter materials are available at: https://github.com/CppCon/CppCon2014
--
This talk revisits basic questions, such as how to declare and initialize a variable, how to pass a value to a function, how to write a simple loop, and how to use smart pointers, in the light of experience with C++11 and the latest C++14 refinements. This involves examining auto, rvalue references, range-for loops, uniform initialization, lambda expressions, unique_ptr and shared_ptr, and more.
--
Herb Sutter - Author, chair of the ISO C++ committee, software architect at Microsoft.
--
Videos Filmed & Edited by Bash Films: http://www.BashFilms.com

Comments (7):

By anonymous    2017-09-20

You are not comparing like-with-like

If you are writing a move-only type like std::unique_ptr then a move assignment operator would be your only choice.

The more typical case is where you have a copyable type in which case I think you have three options.

  1. T& operator=(T const&)
  2. T& operator=(T const&) and T& operator=(T&&)
  3. T& operator=(T) and move

Note that having both the overloads you suggested in one class is not an option as it would be ambiguous.

Option 1 is the traditional C++98 option and will perform fine in most cases. However, if you need to optimize for r-values you could consider Option 2 and add a move assignment operator.

It is tempting to consider Option 3 and pass-by-value and then move which I think is what you are suggesting. In that case you only have to write one assignment operator. It accepts l-values and at the cost of only one extra move accepts r-values and many people will advocate this approach.

However, Herb Sutter pointed out in his "Back to the Basics! Essentials of Modern C++ Style" talk at CppCon 2014 that this option is problematic and can be much slower. In the case of l-values it will perform an unconditional copy and will not reuse any existing capacity. He provides numbers to backup his claims. The only exception is constructors where there is no existing capacity to reuse and you often have many parameters so pass by-value can reduce the number of overloads needed.

So I would suggest you start with Option 1 and move to Option 2 if you need to optimize for r-values.

Original Thread

By anonymous    2017-09-20

Since your question is about performance and readability, I suggest you listen to Herb Sutter's speech @ CppCon 2014 here.

In a nutshell:

  • Write for clarity and correctness first.
  • Avoid premature optimization (prefer clear code over optimized code).

I don't think memory optimization is a concern for a chess program IMHO.

Original Thread

By anonymous    2017-09-20

Herb Sutter's advice on this is to start with the standard C++98 approach:

void setString(const std::string& str) {
  str_ = str;
}

And if you need to optimize for rvalues add an overload that takes an rvalue reference:

void setString(std::string&& str) noexcept {
  str_ = std::move(str);
}

Note that most implementations of std::string use the small string optimization so that if your strings are small a move is the same as a copy anyway and you wouldn't get any benefit.

It is tempting to use pass-by-value and then move (as in Adam Hunyadi's answer) to avoid having to write multiple overloads. But Herb pointed out that it does not re-use any existing capacity of str_. If you call it multiple times with lvalues it will allocate a new string each time. If you have a const std::string& overload then it can re-use existing capacity and avoid allocations.

If you are really clever you can use a templated setter that uses perfect forwarding but to get it completely correct is actually quite complicated.

Original Thread

By anonymous    2017-10-22

Book.h

#pragma once
#include <string>

class Book
{
public:

    Book() = default;
    ~Book() = default;

    const std::string GetTitle() const;
    const std::string GetAuthor() const;
    const int GetCopyRightYear() const;

    void SetTitle(const std::string);
    void SetAuthor(const std::string);
    void SetCopyRightYear(const int);
    void PrintBook();

private:
    std::string title;
    std::string author;
    int copyright_year;
};

Book.cpp

#include "Book.h"
// ------------------------------
#include <iostream>




void Book::SetTitle(const std::string title_input)
{
    title = title_input;
}



const std::string Book::GetTitle() const
{
    return title;
}



const int Book::GetCopyRightYear() const
{
    return copyright_year;
}



const std::string Book::GetAuthor() const
{
    return author;
}



void Book::SetCopyRightYear(const int copyright_year_input)
{
    copyright_year = copyright_year_input;
}



void Book::SetAuthor(const std::string author_input)
{
    author = author_input;
}



void Book::PrintBook()
{
    std::string output_str = "";
    std::cout << "Title of Book: " << GetTitle() << std::endl;
    std::cout << "Author of Book: " << GetAuthor() << std::endl;
    std::cout << "Copyright Year: " << GetCopyRightYear() << std::endl;
}

main.cpp

// C++ Libraries.
#include <iostream>
#include <string>

// User classes
#include "Book.h"

// Namespaces

int main()
{
    std::string title_input = "";
    std::string author_input = "";
    int copyright_year_input = 0;

    // research dynamic memory allocation.
    Book book1;
    Book book2;
    Book book3;
    Book book4;


    // user sets book title.
    std::cout << "Enter the book title: ";
    std::getline(std::cin, title_input);
    book1.SetTitle(title_input);

    // user sets the authors name
    std::cout << "Enter the author name: ";
    std::getline(std::cin, author_input);
    book1.SetAuthor(author_input);

    // user inputs the copyright year.
    std::cout << "Enter the copyright year: ";
    std::cin >> copyright_year_input;
    book1.SetCopyRightYear(copyright_year_input);

    // Display the information.
    book1.PrintBook();
}

Notes:

  • When you start using multiple namespaces its easier to see what is what if you dont predefine them.
  • Const correctness means you and other developers know what can be changed and what cant. It also makes things clearer for the compiler.
  • std::getline reads the whole line including the blank spaces.

Just a quick note on clarity and understanding. At the moment your code is messy which makes it incredibly hard to debug not only for yourself but for others.

I can't tell on here but just in case, your classes should be in header and source code formatting, with a main source code file for the main function (entry point). Whether or not you've been told this information before I would highly recommend doing some research into basic C++. Just for starters I've put some links below to help. Once your code is neatly formatted you might work out what the problem is.

Happy coding :)

References: Herb Sutter Cpp Convention 2014 - Simplicity over Complexity: https://www.youtube.com/watch?v=xnqTKD8uD64

Headers and Includes - C++ formatting: http://www.cplusplus.com/forum/articles/10627/

Also see the tutorials on cplusplus.com.

Original Thread

Recommended Books

    Submit Your Video

    If you have some great dev videos to share, please fill out this form.