auto&&… args.

Kiedyś zastanawiałem się nad tym, co jeszcze można będzie zrobić z auto? Auto może być użyte do dedukcji typów uniwersalnych referencji pakietu parametrów, w funkcjach o dowolnej liczbie parametrów. Brzmi strasznie ale wbrew pozorom nie jest to takie skomplikowane:

#include <iostream>

using namespace std;

int main()
{
        auto f = [] (auto ... args) {
            ((cout << args << " "), ...);
            cout << endl;
        };

        f(1, 2.3, "cztery");
}

~/cpp_fun>$ ./”variadic_lambdas1″
1 2.3 cztery

Niedługo mam nadzieję będzie to działać nie tylko dla funkcji lambda, ale też dla „zwykłych” funkcji.

std::variant, std::any i ranges-v3 czyli prawie jak w Pythonie.

Ostatnio w ramach „się odstresowywania” czytam sobie o zmianach w standardzie c++17 i c++20 (wielkie dzięki Bartkowi Filipkowi za jego bloga). Dużo tego, ale zmiany są ogromne i dzisiejszy C++ jest po prostu fascynujący. Nawet nie komentuje poniższego kodu. Wklejam tu dla samego siebie i powiem tylko, że jak g++/clang++ będą z c++20 zgodne, to ja już nie chcę nic więcej. Może jeszcze tylko ogarnięcie parallelstl. Intel-tbb działa pięknie. Tylko jeden cytat apropos:

„Do you know how much of the processing power of a typical desktop machine we can utilize using only the core version of C++/Standard Library? 50%, 100%? 10%?[…] we can usually access only around 0,25% with single-threaded C++ code and maybe a few percent when you add threading from C++11/14.” (Sean Parent).

Przyznam szczerze, że nieco otworzyło mi to oczy:

GPU power CPU vectorization CPU threading Single Thread
75% 20% 4% 0,25%
#include <iostream>
#include <vector>
#include <variant>
#include <any>

using namespace std;

template <typename T1, typename T2>
class T {
    T1 a; T2 b;    
    public:
    T(T1 a, T2 b) : a(a), b(b) {}
    void show() {        
        cout << a << b;
    }    
};

int main() {
    T<char, const char*> t1('C', "++");
    T<int, const char*> t2(17, " nie taki straszny ;)\n");
    T<string, int> t3("66.", 6);

    typedef variant<T<char, const char*>, T<int, const char*>, T<string, int>> var_t;
    vector<var_t> var_v {t1, t2, t3};

    auto callShow = [](auto& obj) { obj.show(); };

    for (auto& obj : var_v)
        std::visit(callShow, obj);   

    cout << endl << "------" << endl;

    vector<any>anyv {1, 2.0, "trzy"};

    for(auto &i: anyv) {
        if (i.type() == typeid(int))
            cout << any_cast<int>(i) << endl;     
        if (i.type() == typeid(double))
            cout << any_cast<double>(i) << endl;     
        if (i.type() == typeid(const char *))
            cout << any_cast<const char *>(i) << endl;   
        if (i.type() == typeid(string))
            cout << any_cast<string>(i) << endl;    
    }
}

PS E:\cpp_fun> .\templ1.exe
C++17 nie taki straszny 😉
66.6
——
1
2
trzy

#include <iostream>
#include <range/v3/all.hpp> 
#include <string>
#include <vector>

using namespace std;
using namespace ranges;

int main()
{
    auto print = [](auto &i) {cout << i << " "; };    
    auto even = [](int i){ return 0 == i % 2; };
    auto square = [](int i) { return i * i; };
    auto end = ranges::end;

    vector<int> ints = views::ints(1, 50)  | views::filter(even) | 
                       views::transform(square) | views::slice(end-5, end) | 
                       ranges::to<vector>();

    ranges::for_each(ints, print);

    auto chars = views::iota('a', 'z'+1) | views::reverse
                       | ranges::to<vector>();
    
    ranges::for_each(chars, print);

    auto t = views::ints(0, 50) | 
         views::filter([](int i) { return i<20 ? true : false; }) | 
         ranges::to<vector>();
    
    ranges::for_each(t, print);
    cout << endl;    
}

PS E:\cpp_fun> .\ranges2.exe
1600 1764 1936 2116 2304 z y x w v u t s r q p o n m l k j i h g f e d c b a 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

BTW: dzięki Bartkowi odkryłem, że nie boję się już lambd, a nawet je trochę lubię 😉