trainings/AdvancedCppV2/Presentation/moder_cpp_cpp17_folding.md

6.2 KiB

Fold expressions

  • Folding is a new way of handling argument package
  • It can be one or two arguments
  • If it is two arguments we distinguish between
    • left folding
    • right folding

Fold expressions - adding values

template <typename... Args>
int add(Args... args) {
    return (args + ...);
}

int main() {
    std::cout << add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << '\n';
    std::cout << add() << '\n'; // Not compile!
}
55

Fold expressions - adding values

template <typename... Args>
int add(Args... args) {
    return (args + ... + 0);
}

int main() {
    std::cout << add(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) << '\n';
    std::cout << add() << '\n'; // OK
}
55
0

Fold expressions - subtracting values

template <typename... Args>
int subR(Args... args) {
    return (args - ...);
}

template <typename... Args>
int subL(Args... args) {
    return (... - args);
}

int main() {
    // (1 - 2)
    std::cout << subR(1, 2) << '\n';           // -1
    // (1 - 2)
    std::cout << subL(1, 2) << '\n';           // -1
    // (1 - (2 - 3))
    std::cout << subR(1, 2, 3) << '\n';        //  2
    // ((1 - 2) - 3)
    std::cout << subL(1, 2, 3) << '\n';        // -4
    // (1 - (2 - (3 - 4)))
    std::cout << subR(1, 2, 3, 4) << '\n';     // -2
    // (((1 - 2) - 3) - 4)
    std::cout << subL(1, 2, 3, 4) << '\n';     // -8
    // (1 - (2 - (3 - (4 - 5))))
    std::cout << subR(1, 2, 3, 4, 5) << '\n';  //  3
    // ((((1 - 2) - 3) - 4) - 5)
    std::cout << subL(1, 2, 3, 4, 5) << '\n';  // -13
}

Fold expressions - make code work without parameters

Find a problem with the below code.

template <typename... Args>
int subR(Args... args) {
    return (args - ... - 0);
}

template <typename... Args>
int subL(Args... args) {
    // What's wrong here???
    return (0 - ... - args);
}

int main() {
    // (1 - (2 - (3 - (4 - 5))))
    std::cout << subR(1, 2, 3, 4, 5) << '\n';
    // ((((1 - 2) - 3) - 4) - 5)
    std::cout << subL(1, 2, 3, 4, 5) << '\n';

    std::cout << subR() << '\n';
    std::cout << subL() << '\n';
}
3   // (1 - (2 - (3 - (4 - (5 - 0))))
-15 // (((((0 - 1) - 2) - 3) - 4) - 5)
0
0

How to demand minimum one variable

template<typename Value, typename... Values>
auto average(Value const& value, Values const&... values)
{
    return (value + ... + values) / (1. + sizeof...(values));
}

int main() {
    std::cout << average(1, 2, 3, 4) << '\n'; // print 2.5
    std::cout << average() << '\n' // NOT COMPILE!
}

Calling member functions

template<typename... Args>
void loggStateForAll(const Args&... args)
{
    (..., args.loggState());
}

struct A {
    void loggState() const {
        std::cout << "StateA: good!\n";
    }
};

struct B {
    void loggState() const {
        std::cout << "StateB: bad!\n";
    }
};

int main() {
    loggStateForAll(A{}, B{}, A{}, B{});
    /* Will print
    StateA: good!
    StateB: bad!
    StateA: good!
    StateB: bad! */
}

Fold expressions - insert to container (1)

class Foo {
public:
    Foo(int num)
        : num_(num) {}

    int num() const { return num_; }

private:
    int num_;
};

template <typename... Args>
void emplaceAll(std::vector<Foo>& vec, Args... args) {
    (vec.emplace_back(args), ...);
}

int main() {
    std::vector<Foo> vec;
    emplaceAll(vec, 1, 2, 3, 4, 5, 6, 7);

    std::transform(cbegin(vec), cend(vec), std::ostream_iterator<int>(std::cout, " "),
                   [](const auto& foo) { return foo.num(); });
}
1 2 3 4 5 6 7
  • What with (..., vec.emplace_back(args))?

Fold expressions - insert to container (2)

  • There is no right/left type of folding for one type of argument
    • Unary right fold (fun(arg0) , (fun(arg1) , (fun(arg2) , ...)))
    • Unary left fold (((fun(arg0) , fun(arg1)) , fun(arg2)) , ...
  • Only for binary folding, we can have different behavior

Fold expressions - logic operators

template <typename... Args>
bool emplaceAll(std::set<int>& set, Args... args) {
    return (set.insert(args).second && ...);
}

int main() {
    std::set<int> set;
    emplaceAll(set, 1, 2, 3, 4, 5, 1, 6, 7);

    std::copy(cbegin(set), cend(set), std::ostream_iterator<int>(std::cout, " "));
}
1 2 3 4 5

For bot way: return (set.insert(args).second && ...);

and return (... && set.insert(args).second); output will be the same


Fold expressions - cooperation with STL algorithms

template <typename... Args>
bool HasAll(const std::vector<int>& vec, Args... args) {
    return ((std::find(cbegin(vec), cend(vec), args) != std::cend(vec)) && ...);
}

int main() {
    std::vector<int> vec{1, 2, 3, 4, 1, 2, 3, 5, 6, 7, 4};
    std::cout << std::boolalpha << HasAll(vec, 2, 4, 6) << '\n';
    std::cout << std::boolalpha << HasAll(vec, 2, 4, 6, 8) << '\n';
}
true
false