trainings/AdvancedCppV2/Presentation/moder_cpp_cpp17_small_features.md

6.4 KiB

Nested namespace definitions

You can nest namespaces like this:

namespace A::B::C {
  ...
}

Instead of this:

namespace A {
  namespace B {
    namespace C {
      ...
    }
  }
}

Class template argument deduction

From C++17 class template arguments can be deduced automatically. Automatic template argument deduction was available earlier only for template functions.

std::pair p(1, 'x');                    // C++17: OK, C++14: error: missing.
                                        // std::pair<int, char> 
std::pair<int, std::string> p(1, "x");  // C++14: OK
                                        // std::pair<int, std::string> 
auto p2 = std::make_pair(1, "x");       // C++17: OK, C++14: OK (but not string!)
                                        // std::pair<int, const char*> 
std::pair p3(1, "x");                   // C++17: OK (but not string!), C++14 error

Selection statements with initializer (1)

New versions of the if and switch statements for C++:

if (init; condition)

status_code foo() { // C++14
    { //variable c scope
        status_code c = bar();
        if (c != SUCCESS) {
            return c;
        }
    }
    // ...
}
status_code foo() { // C++17
    if (status_code c = bar(); c != SUCCESS) {
        return c;
    }
    // ...
}

Selection statements with initializer (2)

switch (init; condition)

class Foo {
public:
    enum class ErrorCode {
        Ok,
        Bad,
        VeryBad
    };

    ErrorCode doSth() {}
}

int main() {
    switch (Foo foo ; const auto err = foo.doSth()) {
    case ErrorCode::Ok:
        break;
    case ErrorCode::Bad:
        break;
    case ErrorCode::VeryBad:
        break;
    }
}

Selection statements with initializer (3)

class ThreadSafeQueue {
public:
    std::optional<int> try_pop() {
        int val = 0;
        // Lock guard is visible inside if-else
        if (std::lock_guard lock(m_) ; queue_.empty()) {
            return {};
        } else {
            val = queue_.front();
            queue_.pop();
        }
        
        // Here mutex is no longer locked
        logger << "pop value from queue";
        return val;
    }

private:
    std::queue<int> queue_;
    std::mutex m_;
};

Hiding variable inside if-else statement (2)

We can avoid assigning iterators and then comapre it in if statement. Now we can write it faster

int main() {
    std::vector<int> vec;

    if (const auto it = std::find(std::cbegin(vec), std::cend(vec), 10) ; it != std::cend(vec)) {
        // *it* is visible here
    } else {
        // and here
    }
    // but not here

    std::map<std::string, int>
    if (const auto it = map.find("Ala") ; it != std::cend(map)) {
        // ...
    } else {
        // ...
    }
}

Unified initialization

  • C++11 introduce {} for initialization
  • C++17 fixed some problems with {} and unified it.

Unified initialization C++11

  • Guess output (compiler 4.7.4 with C++11 flag)

auto a {1};             //std::initializer_list<int>
auto b = {1};           //std::initializer_list<int>
auto c {1, 2};          //std::initializer_list<int>
auto d = {1, 2};        //std::initializer_list<int>
auto e = {1, 2, 3.f};   //Not compile               
auto f {1, 2, 3.f}      //Not compile               

Unified initialization C++17

  • Guess output (compiler 8.4 with C++17 flag)

auto a {1};             //int                       
auto b = {1};           //std::initializer_list<int>
auto c {1, 2};          //Not compile (mising =)              
auto d = {1, 2};        //std::initializer_list<int>
auto e = {1, 2, 3.f};   //Not compile               
auto f {1, 2, 3.f}      //Not compile               

Structural biding

  • Unpack strucures, classes, tuples, pairs etc...
struct Foo{};

std::tuple<int, std::string, Foo> getTuple() {
    return {5, "Ala has a cat", Foo{}};
}

struct Bar {
    std::string str_;
    double val_;
    char c_;
    std::vector<int> vec_;
};

int main() {
    const auto& [id, topic, foo] = getTuple();

    std::vector<Bar> bar;
    for (const auto& [name, value, sign, vec] : bar) {
        // ...
    }

    std::map<int, std::string> map;
    for (const auto& [key, value] : map) {
        // ..
    }
};