Szablony¶
Aliasy szablonów¶
Aliasy typów¶
W C++11 deklaracja using
może zostać użyta do tworzenia bardziej czytelnych aliasów dla typów - zamiennik dla typedef
.
using CarID = int;
using Func = int(*)(double, double);
using DictionaryDesc = std::map<std::string, std::string, std::greater<std::string>>;
Aliasy szablonów¶
Aliasy typów mogą być parametryzowane typem. Można je wykorzystać do tworzenia częściowo związanych typów szablonowych.
template <typename T>
using StrKeyMap = std::map<std::string, T>;
StrKeyMap<int> my_map; // std::map<std::string, int>
Parametrem aliasu typu szablonowego może być także stała znana w czasie kompilacji:
template <std::size_t N>
using StringArray = std::array<std::string, N>;
StringArray<255> arr1;
Aliasy szablonów nie mogą być specjalizowane.
template <typename T>
using MyAllocList = std::list<T, MyAllocator>;
template <typename T>
using MyAllocList = std::list<T*, MyAllocator>; // error
Przykład utworzenia aliasu dla szablonu klasy smart pointer’a:
template <typename Stream>
struct StreamDeleter
{
void operator()(Stream* os)
{
os->close();
delete os;
}
}
template <typename Stream>
using StreamPtr = std::unique_ptr<Stream, StreamDeleter<Stream>>;
// ...
{
StreamPtr<ofstream> p_log(new ofstream("log.log"));
*p_log << "Log statement";
}
Aliasy i typename¶
Od C++14 biblioteka standardowa używa aliasów dla wszystkich cech typów, które zwracają typ.
template <typename T>
using is_void_t = typename is_void<T>::type;
W rezultacie kod odwołujący się do cechy:
typename is_void<T>::type
możemy uprościć do:
is_void_t<T>;
Szablony zmiennych¶
W C++14 zmienne mogą być parametryzowane przy pomocy typu. Takie zmienne nazywamy zmiennymi szablonowymi (variable templates).
template<typename T>
constexpr T pi{3.1415926535897932385};
Aby użyć zmiennej szablonowej, należy podać jej typ:
std::cout << pi<double> << '\n';
std::cout << pi<float> << '\n';
Parametrami zmiennych szablonowych mogą być stałe znane na etapie kompilacji:
template<int N>
std::array<int, N> arr{};
int main()
{
arr<10>[0] = 42; // sets first element of global arr
for (std::size_t i = 0; i < arr<10>.size(); ++i)
{
// uses values set in arr
std::cout << arr<10>[i] << '\n';
}
}
Zmienne szablonowe a jednostki translacji¶
Deklaracja zmiennych szablonowych może być używana w innych jednostkach translacji.
plik -
header.hpp
template<typename T> T val{}; // zero initialized value
plik - „unit1.cpp”
#include "header.hpp" int main() { val<long> = 42; print(); }
plik - „unit2.cpp”
void print() { std::cout << val<long> << '\n'; // OK: prints 42 }
Variadic templates¶
W C++11 szablony mogą akceptować dowolną ilość (również zero) parametrów. Jest to możliwe dzięki użyciu specjalnego grupowego parametru szablonu tzw. parameter pack, który reprezentuje wiele lub zero parametrów szablonu.
Parameter pack¶
Parameter pack może być
grupą parametrów szablonu
template<typename... Ts> // template parameter pack class tuple { //... }; tuple<int, double, string&> t1; // 3 arguments: int, double, string& tuple<> empty_tuple; // 0 arguments
grupą argumentów funkcji szablonowej
template <typename T, typename... Args> shared_ptr<T> make_shared(Args&&... params) { //... } auto sptr = make_shared<Gadget>(10); // Args as template param: int // Args as function param: int&&
Rozpakowanie paczki parametrów¶
Podstawową operacją wykonywaną na grupie parametrów szablonu jest rozpakowanie jej za pomocą operatora ...
(tzw. pack expansion).
template <typaname... Ts> // template parameter pack
struct X
{
tuple<Ts...> data; // pack expansion
};
Rozpakowanie paczki parametrów (pack expansion) może zostać zrealizowane przy pomocy wzorca zakończonego elipsą ...
:
template <typaname... Ts> // template parameter pack
struct XPtrs
{
tuple<Ts const*...> ptrs; // pack expansion
};
XPtrs<int, string, double> ptrs; // contains tuple<int const*, string const*, double const*>
Najczęstszym przypadkiem użycia wzorca przy rozpakowaniu paczki parametrów jest implementacja perfect forwarding’u:
template <typename... Args>
void make_call(Args&&... params)
{
callable(forward<Args>(params)...);
}
Idiom Head/Tail¶
Praktyczne użycie variadic templates wykorzystuje często idiom Head/Tail (znany również First/Rest).
Idiom ten polega na zdefiniowaniu wersji szablonu akceptującego dwa parametry:
- pierwszego Head
- drugiego Tail
w postaci paczki parametrów
W implementacji wykorzystany jest parametr (lub argument) typu Head
, po czym rekursywnie wywołana jest implementacja dla rozpakowanej
paczki parametrów typu Tail
.
Dla szablonów klas idiom wykorzystuje specjalizację częściową i szczegółową (do przerwania rekurencji):
template <typename... Types>
struct Count;
template <typename Head, typename... Tail>
struct Count<Head, Tail...>
{
constexpr static int value = 1 + Count<Tail...>::value; // expansion pack
};
template <>
struct Count<>
{
constexpr static int value = 0;
};
//...
static_assert(Count<int, double, string&>::value == 3, "must be 3");
W przypadku szablonów funkcji rekurencja może być przerwana przez dostarczenie odpowiednio przeciążonej funkcji. Zostanie ona w odpowiednim momencie rozwijania rekurencji wywołana.
void print()
{}
template <typename T, typename... Tail>
void print(const T& arg1, const Tail&... params)
{
cout << arg1 << endl;
print(params...); // function parameter pack expansion
}
Operator sizeof…¶
Operator sizeof...
umożliwia odczytanie na etapie kompilacji ilości parametrów w grupie.
template <typename... Types>
struct VerifyCount
{
static_assert(Count<Types...>::value == sizeof...(Types),
"Error in counting number of parameters");
};
Forwardowanie wywołań funkcji¶
Variadic templates są niezwykle przydatne do forwardowania wywołań funkcji.
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... params)
{
return std::unique_ptr<T>(new T(std::forward<Args>(params)...);
}
Ograniczenia paczek parametrów¶
Klasa szablonowa może mieć tylko jedną paczkę parametrów i musi ona zostać umieszczona na końcu listy parametrów szablonu:
template <size_t... Indexes, typename... Ts> // error
class Error;
Można obejść to ograniczenie w następujący sposób:
template <size_t... Indexes> struct IndexSequence {};
template <typename Indexes, typename Ts...>
class Ok;
Ok<IndexSequence<1, 2, 3>, int, char, double> ok;
Funkcje szablonowe mogą mieć więcej paczek parametrów:
template <int... Factors, typename... Ts>
void scale_and_print(Ts const&... args)
{
print(ints * args...);
}
scale_and_print<1, 2, 3>(3.14, 2, 3.0f); // calls print(1 * 3.14, 2 * 2, 3 * 3.0)
Uwaga! Wszystkie paczki w tym samym wyrażeniu rozpakowującym muszą mieć taki sam rozmiar.
scale_and_print<1, 2>(3.14, 2, 3.0f); // error
„Nietypowe” paczki parametrów¶
- Podobnie jak w przypadku innych parametrów szablonów, paczka parametrów nie musi być paczką typów, lecz może być paczką stałych znanych na etapie kompilacji
template <size_t... Values>
struct MaxValue; // primary template declaration
template <size_t First, size_t... Rest>
struct MaxValue<First, Rest...>
{
static constexpr size_t rvalue = MaxValue<Rest...>::value;
static constexpr size_t value = (First < rvalue) ? rvalue : First;
};
template <size_t Last>
struct MaxValue<Last>
{
static constexpr size_t value = Last; // termination of recursive expansion
};
static_assert(MaxValue<1, 5345, 3, 453, 645, 13>::value == 5345, "Error");
Variadic Mixins¶
Variadic templates mogą być skutecznie wykorzystane do implementacji klas mixin
#include <vector>
#include <string>
template <typename... Mixins>
class X : public Mixins...
{
public:
X(Mixins&&... mixins) : Mixins(mixins)...
{}
};
Klasa taka może zostać wykorzystana później w następujący sposób:
X<std::vector<int>, std::string> x({ 1, 2, 3 }, "text");
x.std::string::size();
(unsigned long) 4
x.std::vector<int>::size();
(unsigned long) 3
Fold expressions (C++17)¶
Wyrażenia fold w językach funkcjonalnych¶
Koncept redukcji jest jednym z podstawowych pojęć w językach funkcjonalnych.
Fold w językach funkcjonalnych to rodzina funkcji wyższego rzędu zwana również reduce, accumulate, compress lub inject. Funkcje fold przetwarzają rekursywnie uporządkowane kolekcje danych (listy) w celu zbudowania końcowego wyniku przy pomocy funkcji (operatora) łączącej elementy.
Dwie najbardziej popularne funkcje z tej rodziny to fold (fold left) i foldr (fold right).
Przykład:
Redukcja listy [1, 2, 3, 4, 5] z użyciem operatora (+):
użycie funkcji
fold
- redukcja od lewej do prawejfold (+) 0 [1..5]
(((((0 + 1) + 2) + 3) + 4) + 5)
użycie funkcji
foldr
- redukcja od prawej do lewejfoldr (+) 0 [1..5]
(1 + (2 + (3 + (4 + (5 + 0)))))
Redukcja w C++98 - std::accumulate¶
W C++ redukcja jest obecna poprzez implementację algorytmu
std::accumulate
.
#include <vector>
#include <numeric>
#include <string>
using namespace std::string_literals;
std::vector<int> vec = {1, 2, 3, 4, 5};
std::accumulate(std::begin(vec), std::end(vec), "0"s,
[](const std::string& reduction, int item) {
return "("s + reduction + " + "s + std::to_string(item) + ")"s; });
(((((0 + 1) + 2) + 3) + 4) + 5)
Wyrażenia fold¶
Wyrażenia typu fold umożliwiają uproszczenie rekurencyjnych implementacji dla zmiennej liczby argumentów szablonu.
Przykład z wariadyczną funkcją sum(1, 2, 3, 4, 5)
z wykorzystaniem
fold expressions może być w C++17 zaimplementowany następująco:
template <typename... Args>
auto sum(Args&&... args)
{
return (... + args);
}
sum(1, 2, 3, 4, 5);
(int) 15
Składnia wyrażeń fold¶
Niech \(e = e_1, e_2, \dotso, e_n\) będzie wyrażeniem, które zawiera nierozpakowany parameter pack i \(\otimes\) jest operatorem fold, wówczas wyrażenie fold ma postać:
Unary left fold
\((\dotso\; \otimes\; e)\)
który jest rozwijany do postaci \((((e_1 \otimes e_2) \dotso ) \otimes e_n)\)
Unary right fold
\((e\; \otimes\; \dotso)\)
który jest rozwijany do postaci \((e_1 \otimes ( \dotso (e_{n-1} \otimes e_n)))\)
Jeśli dodamy argument nie będący paczką parametrów do operatora ...
,
dostaniemy dwuargumentową wersję wyrażenia fold. W zależności od
tego po której stronie operatora ...
dodamy dodatkowy argument
otrzymamy:
Binary left fold
\((a \otimes\; \dotso\; \otimes\; e)\)
który jest rozwijany do postaci \((((a \otimes e_1) \dotso ) \otimes e_n)\)
Binary right fold
\((e\; \otimes\; \dotso\; \otimes\; a)\)
który jest rozwijany do postaci \((e_1 \otimes ( \dotso (e_n \otimes a)))\)
Operatorem \(\otimes\) może być jeden z poniższych operatorów C++:
+ - * / % ^ & | ~ = < > << >>
+= -= *= /= %= ^= &= |= <<= >>=
== != <= >= && || , .* ->*
Elementy identycznościowe¶
Operacja fold dla pustej paczki parametrów (parameter pack) jest ewaluowana do określonej wartości zależnej od rodzaju zastosowanego operatora. Zbiór operatorów i ich rozwinięć dla pustej listy parametrów prezentuje tabela:
Operator | Wartość zwracana jako element identycznościowy |
---|---|
\(\&\&\) | true |
\(\mid\mid\) | false |
\(,\) | void() |
Jeśli operacja fold jest ewaluowana dla pustej paczki parametrów dla innego operatora, program jest nieprawidłowo skonstruowany (ill-formed).
Przykłady zastosowań wyrażeń fold¶
Wariadyczna funkcja przyjmująca dowolną liczbę argumentów
konwertowalnych do wartości logicznych i zwracająca ich iloczyn logiczny
(operator &&
):
template <typename... Args>
bool all_true(Args... args)
{
return (... && args);
}
bool result = all_true(true, true, false, true);
(bool) false
Funkcja print()
wypisująca przekazane argumenty. Implementacja
wykorzystuje wyrażenie binary left fold dla operatora <<
:
#include <iostream>
template <typename... Args>
void print(Args&&... args)
{
(std::cout << ... << args) << "\n";
}
print(1, 2, 3, 4);
1234
Iteracja po elementach różnych typów:
#include <iostream>
struct Window {
void show() const { std::cout << "showing Window\n"; }
};
struct Widget {
void show() const { std::cout << "showing Widget\n"; }
};
struct Toolbar {
void show() const { std::cout << "showing Toolbar\n"; }
};
Window wnd;
Widget widget;
Toolbar toolbar;
auto printer = [](const auto&... args) { args.show(), ...); };
printer(wnd, widget, toolbar);
showing Window
showing Widget
showing Toolbar
Implementacja wariadycznej wersji algorytmu foreach()
z
wykorzystaniem funkcji std::invoke()
:
#include <iostream>
template <typename F, typename... Args>
auto invoke(F&& f, Args&&... args)
{
return std::forward<F>(f)(std::forward<Args>(args)...);
}
struct Printer
{
template <typename T>
void operator()(T&& arg) const { std::cout << arg; }
};
#include <string>
using namespace std::literals;
auto foreach = [](auto&& fun, auto&&... args) {
(..., invoke(fun, std::forward<decltype(args)>(args));
};
foreach(Printer{}, 1, " - one; ", 3.14, " - pi;"s);
1 - one; 3.14 - pi
Dedukcja argumentów szablonu dla klas - CTAD (C++17)¶
C++17 wprowadza mechanizm dedukcji argumentów szablonu klasy (Class Template Argument Deduction). Typy parametrów szablonu klasy mogą być dedukowane na podstawie argumentów przekazanych do konstruktora tworzonego obiektu.
template <typename T>
class complex
{
T re_, img_;
public:
complex(T re, T img) : re_{re}, img_{img}
{}
};
auto c1 = complex<int>{5, 2}; // OK - all versions of C++ standard
auto c2 = complex{5, 3}; // OK since C++17 - compiler deduces complex<int>
auto c3 = complex(5.1, 6.5); // OK in C++17 - compiler deduces complex<double>
auto c4 = complex{5, 4.1}; // ERROR - args don't have the same type
auto c5 = complex(5, 4.1); // ERROR - args don't have the same type
Ostrzeżenie
Nie można częściowo dedukować argumentów szablonu klasy. Należy wyspecyfikować lub wydedukować wszystkie parametry z wyjątkiem parametrów domyślnych.
Praktyczny przykład dedukcji argumentów szablonu klasy:
std::mutex mtx;
std::lock_guard lk{mtx}; // deduces std::lock_guard<std::mutex>
Podpowiedzi dedukcyjne (deduction guides)¶
C++17 umożliwia tworzenie podpowiedzi dla kompilatora, jak powinny być dedukowane typy parametrów szablonu klasy na podstawie wywołania odpowiedniego konstruktora.
Daje to możliwość poprawy/modyfikacji domyślnego procesu dedukcji.
Dla szablonu:
template <typename T>
class S
{
private:
T value;
public:
S(T v) : value(v)
{}
};
Podpowiedź dedukcyjna musi zostać umieszczona w tym samym zakresie (przestrzeni nazw) i może mieć postać:
template <typename T> S(T) -> S<T>; // deduction guide
gdzie:
S<T>
to tzw. typ zalecany (guided type)- nazwa podpowiedzi dedukcyjnej musi być niekwalifikowaną nazwą klasy szablonowej zadeklarowanej wcześniej w tym samym zakresie
- typ zalecany podpowiedzi musi odwoływać się do identyfikatora szablonu (template-id), do którego odnosi się podpowiedź
Użycie podpowiedzi:
S x{12}; // OK -> S<int> x{12};
S y(12); // OK -> S<int> y(12);
auto z = S{12}; // OK -> auto z = S<int>{12};
S s1(1), s2{2}; // OK -> S<int> s1(1), s2{2};
S s3(42), s4{3.14}; // ERROR
W deklaracji S x{12};
specyfikator S
jest nazywany symbolem zastępczym dla klasy (placeholder class type).
W przypadku użycia symbolu zastępczego dla klasy, nazwa zmiennej musi zostać podana jako następny element składni. W rezultacie poniższa deklaracja jest błędem składniowym:
S* p = &x; // ERROR - syntax not permitted
Dany szablon klasy może mieć wiele konstruktorów oraz wiele podpowiedzi dedukcyjnych:
template <typename T>
struct Data
{
T value;
using type1 = T;
Data(const T& v)
: value(v)
{
}
template <typename ItemType>
Data(initializer_list<ItemType> il)
: value(il)
{
}
};
template <typename T>
Data(T)->Data<T>;
template <typename T>
Data(initializer_list<T>)->Data<vector<T>>;
Data(const char*) -> Data<std::string>;
//...
Data d1("hello"); // OK -> Data<string>
const int tab[10] = {1, 2, 3, 4};
Data d2(tab); // OK -> Data<const int*>
Data d3 = 3; // OK -> Data<int>
Data d4{1, 2, 3, 4}; // OK -> Data<vector<int>>
Data d5 = {1, 2, 3, 4}; // OK -> Data<vector<int>>
Data d6 = {1}; // OK -> Data<vector<int>>
Data d7(d6); // OK - copy by default rule -> Data<vector<int>>
Data d8{d6, d7}; // OK -> Data<vector<Data<vector<int>>>>
Podpowiedzi dedukcyjne nie są szablonami funkcji - służą jedynie dedukowaniu argumentów szablonu i nie są wywoływane. W rezultacie nie ma znaczenia czy argumenty w deklaracjach dedukcyjnych są przekazywane przez referencje, czy nie.
template <typename T>
struct X
{
//...
};
template <typename T>
struct Y
{
Y(const X<T>&);
Y(X<T>&&);
};
template <typename T> Y(X<T>) -> Y<T>; // deduction guide without references
W powyższym przykładzie podpowiedź dedukcyjna nie odpowiada dokładnie sygnaturom konstruktorów przeciążonych. Nie ma to znaczenia, ponieważ jedynym celem podpowiedzi jest umożliwienie dedukcji typu, który jest parametrem szablonu. Dopasowanie wywołania przeciążonego konstruktora odbywa się później.
Niejawne podpowiedzi dedukcyjne¶
Ponieważ często podpowiedź dedukcyjna jest potrzebna dla każdego konstruktora klasy, standard C++17 wprowadza mechanizm niejawnych podpowiedzi dedukcyjnych (implicit deduction guides). Działa on w następujący sposób:
- Lista parametrów szablonu dla podpowiedzi zawiera listę parametrów z szablonu klasy - w przypadku szablonowego konstruktora klasy kolejnym elementem jest lista parametrów szablonu konstruktora klasy
- Parametry „funkcyjne” podpowiedzi są kopiowane z konstruktora lub konstruktora szablonowego
- Zalecany typ w podpowiedzi jest nazwą szablonu z argumentami, które są parametrami szablonu wziętymi z klasy szablonowej
Dla klasy szablonowej rozważanej powyżej:
template <typename T>
class S
{
private:
T value;
public:
S(T v) : value(v)
{}
};
niejawna podpowiedź dedukcyjna będzie wyglądać następująco:
template <typename T> S(T) -> S<T>; // implicit deduction guide
W rezultacie programista nie musi implementować jej jawnie.
Specjalny przypadek dedukcji argumentów klasy szablonowej¶
Rozważmy następujący przypadek dedukcji:
S x{42}; // x has type S<int>
S y{x};
S z(x);
W obu przypadkach dedukowany typ zmiennych y
i z
to S<int>
. Mechanizm dedukcji argumentów klasy szablonowej
dedukuje typ taki sam jak typ oryginalnego obiektu a następnie wywoływany jest konstruktor kopiujący.
- dla deklaracji
S<T> x;
S{x}
dedukuje typ:S<T>{x}
zamiastS<S<T>>{x}
W niektórych przypadkach może być to zaskakujące i kontrowersyjne:
std::vector v{1, 2, 3}; // vector<int>
std::vector data1{v, v}; // vector<vector<int>>
std::vector data2{v}; // vector<int>!
W powyższym kodzie dedukcja argumentów szablonu vector
zależy od ilości argumentów przekazanych do konstruktora!
Agregaty a dedukcja argumentów¶
Jeśli szablon klasy jest agregatem, to mechanizm automatycznej dedukcji argumentów szablonu wymaga napisania jawnej podpowiedzi dedukcyjnej.
Bez podpowiedzi dedukcyjnej dedukcja dla agregatów nie działa:
template <typename T>
struct Aggregate1
{
T value;
};
Aggregate1 agg1{8}; // ERROR
Aggregate1 agg2{"eight"}; // ERROR
Aggregate1 agg3 = 3.14; // ERROR
Gdy napiszemy dla agregatu podpowiedź, to możemy zacząć korzystać z mechanizmu dedukcji:
template <typename T>
struct Aggregate2
{
T value;
};
template <typename T>
Aggregate2(T) -> Aggregate2<T>;
Aggregate2 agg1{8}; // OK -> Aggregate2<int>
Aggregate2 agg2{"eight"}; // OK -> Aggregate2<const char*>
Aggregate2 agg3 = { 3.14 }; // OK -> Aggregate2<double>
Podpowiedzi dedukcyjne w bibliotece standardowej¶
Dla wielu klas szablonowych z biblioteki standardowej dodano podpowiedzi dedukcyjne w celu ułatwienia tworzenia instancji tych klas.
std::pair<T>¶
Dla pary STL dodana w standardzie podpowiedź to:
template<class T1, class T2>
pair(T1, T2) -> pair<T1, T2>;
pair p1(1, 3.14); // -> pair<int, double>
pair p2{3.14f, "text"s}; // -> pair<float, string>
pair p3{3.14f, "text"}; // -> pair<float, const char*>
int tab[3] = { 1, 2, 3 };
pair p4{1, tab}; // -> pair<int, int*>
std::tuple<T…>¶
Szablon std::tuple
jest traktowany podobnie jak std::pair
:
template<class... UTypes>
tuple(UTypes...) -> tuple<UTypes...>;
template<class T1, class T2>
tuple(pair<T1, T2>) -> tuple<T1, T2>;
//... other deduction guides working with allocators
int x = 10;
const int& cref_x = x;
tuple t1{x, &x, cref_x, "hello", "world"s}; -> tuple<int, int*, int, const char*, string>
std::optional<T>¶
Klasa std::optional
jest traktowana podobnie do pary i krotki.
template<class T> optional(T) -> optional<T>;
optional o1(3); // -> optional<int>
optional o2 = o1; // -> optional<int>
Inteligentne wskaźniki¶
Dedukcja dla argumentów konstruktora będących wskaźnikami jest zablokowana:
int* ptr = new int{5};
unique_ptr uptr{ip}; // ERROR - ill-formed (due to array type clash)
Wspierana jest dedukcja przy konwersjach:
z
weak_ptr
/unique_ptr
doshared_ptr
:template <class T> shared_ptr(weak_ptr<T>) -> shared_ptr<T>; template <class T, class D> shared_ptr(unique_ptr<T, D>) -> shared_ptr<T>;
z
shared_ptr
doweak_ptr
template<class T> weak_ptr(shared_ptr<T>) -> weak_ptr<T>;
unique_ptr<int> uptr = make_unique<int>(3);
shared_ptr sptr = move(uptr); -> shared_ptr<int>
weak_ptr wptr = sptr; // -> weak_prt<int>
shared_ptr sptr2{wptr}; // -> shared_ptr<int>
std::function¶
Dozwolone jest dedukowanie sygnatur funkcji dla std::function
:
int add(int x, int y)
{
return x + y;
}
function f1 = &add;
assert(f1(4, 5) == 9);
function f2 = [](const string& txt) { cout << txt << " from lambda!" << endl; };
f2("Hello");
Kontenery i sekwencje¶
Dla kontenerów standardowych dozwolona jest dedukcja typu kontenera dla konstruktora akceptującego parę iteratorów:
vector<int> vec{ 1, 2, 3 };
list lst(vec.begin(), vec.end()); // -> list<int>
Dla std::array
dozwolona jest dedukcja z sekwencji:
std::array arr1 { 1, 2, 3 }; // -> std::array<int, 3>