Variadic template
W C++11 do szablonów można przekazywać nie tylko typy ale też wartości (non-type template parameter). Przykład poniżej pokazuje obliczanie średniej w tych dwóch wersjach. Dodatkowo pojawia się nowy sizeof...(), który potrafi powiedzieć z iloma parametrami mamy do czynienia.#include <iostream> using namespace std; template <typename T> int add(T last) { return last; } template <typename T, typename... Nums> int add(T first, Nums... nums) { return first + add(nums...); } // non-type template parameter template<int first, int... Nums> double average1() { return (static_cast<double>(first) + add(Nums...)) / (sizeof...(Nums) + 1); } // type template parameter template <typename T, typename... Args> double average2(T first, Args... args) { return (static_cast<double>(first) + add(args...)) / (sizeof...(Args) + 1); } int main() { cout << average1<1, 2, 3, 4>() << endl; cout << average2(1, 2, 3, 4) << endl; }Wynik:
2.5 2.5
Fold expression
W C++17 wprowadzono fold expression, które pozwala na stosowanie operatorów dla pakietu parametrów. W przykładzie ze średnią, nie trzeba więc stosować rekurencji. Trochę dziwne jest to że kompilator wymaga dwóch nawiasów, jeżeli chcemy rzutować wynik sumowania do double.#include <iostream> using namespace std; template <typename... Args> double average(Args... args) { return static_cast<double>((args + ...)) / sizeof...(Args); } int main() { cout << average(1, 2, 3, 4) << endl; }Wynik:
2.5
Zadanie domowe
Na GoingNative 2012 Andrei Alexandrescu, przedstawił swój wykład "Variadic Templates are Funadic". Andrei bardzo lubi templejty, co widać ;). Variadic template mają w zamyśle być następcą/lepszą wersją wielokropka ("...") obecnego od czasów C, korzysta z niego np. printf(), z którego każdy lubi korzystać. Do lepszego zrozumienia mechanizmu rozwijania variadic template, autor zaproponował zadanie domowe - produkt kartezjański. Ale, żeby się do niego zabrać, trzeba było zrozumieć wykład (sic!), z pomocą przyszedł poniższy link, gdzie, ktoś rozwinął w kod, to co przedstawił Andrei na slajdach:Wpierw, trzeba zmusić kompilator/CMake (gcc 4.6.3) do pracy z nowym C++11, więc poniżej modyfikacja CMakeList.txt
project(variadic_cpp11) cmake_minimum_required(VERSION 2.8) aux_source_directory(. SRC_LIST) add_executable(${PROJECT_NAME} ${SRC_LIST}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")A teraz moje rozwiązanie - tworzą się pary wartości trzech typów i działa tylko dla wersji "A x A". W linijce 25 rozwinięcie, które było potrzebne, aby to osiągnąć. Aby można było przemnożyć między sobą dwa zbiory, trzeba by chyba skorzystać z nowego std::tuple. Ale w to się jeszcze nie wgłębiałem.
#include <iostream> using namespace std; template<class T> int cartesianMult(T t) { return 0; } template<class T, class U, class... Us> int cartesianMult(T t, U u, Us... us) { cout << endl << " (" << t << ", " << u << "),"; cartesianMult<T, Us...>(t, us...); return 0; } template <class... T> void fun(T...) {} template <class... Ts> void cartesianProduct(Ts... ts) { cout << "{"; fun( cartesianMult(ts, ts...)... ); cout << endl << "}" << endl; } int main() { cartesianProduct(1, 3.14, "abc"); return 0; }A oto wyniki i ciekawostka. Typy, a więc i wartości brane są w odwrotnej kolejności, niż były wkładane do funkcji.
{ (abc, 1), (abc, 3.14), (abc, abc), (3.14, 1), (3.14, 3.14), (3.14, abc), (1, 1), (1, 3.14), (1, abc), }
Brak komentarzy:
Prześlij komentarz