Jakie są powody istnienia std::decay
? W jakich sytuacjach std::decay
przydaje się?
decay_t<decltype(...)>
to fajna kombinacja, aby zobaczyć, co auto
by to wydedukować.
Jakie są powody istnienia std::decay
? W jakich sytuacjach std::decay
przydaje się?
decay_t<decltype(...)>
to fajna kombinacja, aby zobaczyć, co auto
by to wydedukować.
Odpowiedzi:
<joke> Oczywiście służy do rozkładania std::atomic
typów radioaktywnych na nieradioaktywne. </joke>
N2609 to papier, który zaproponował std::decay
. Artykuł wyjaśnia:
Mówiąc najprościej,
decay<T>::type
to transformacja typu tożsamości, chyba że T jest typem tablicy lub odniesieniem do typu funkcji. W takich przypadkachdecay<T>::type
zwraca odpowiednio wskaźnik lub wskaźnik do funkcji.
Motywującym przykładem jest C ++ 03 std::make_pair
:
template <class T1, class T2>
inline pair<T1,T2> make_pair(T1 x, T2 y)
{
return pair<T1,T2>(x, y);
}
który zaakceptował jego parametry według wartości, aby literały łańcuchowe działały:
std::pair<std::string, int> p = make_pair("foo", 0);
Jeśli zaakceptuje swoje parametry przez odniesienie, to T1
zostanie wydedukowane jako typ tablicy, a następnie zbudowanie a pair<T1, T2>
będzie źle sformułowane.
Ale oczywiście prowadzi to do znacznej nieefektywności. Stąd potrzeba decay
zastosowania zestawu transformacji, które występują, gdy wystąpi przekazanie przez wartość, co pozwala uzyskać efektywność pobierania parametrów przez odniesienie, ale nadal uzyskać transformacje typu potrzebne do działania kodu z literałami ciągów, typy tablic, typy funkcji i tym podobne:
template <class T1, class T2>
inline pair< typename decay<T1>::type, typename decay<T2>::type >
make_pair(T1&& x, T2&& y)
{
return pair< typename decay<T1>::type,
typename decay<T2>::type >(std::forward<T1>(x),
std::forward<T2>(y));
}
Uwaga: nie jest to rzeczywista make_pair
implementacja C ++ 11 - C ++ 11 make_pair
również rozpakowuje std::reference_wrapper
s.
W przypadku funkcji szablonu, które pobierają parametry typu szablonu, często masz parametry uniwersalne. Parametry uniwersalne są prawie zawsze referencjami takiego czy innego rodzaju. Są również wykwalifikowani. W związku z tym większość cech typu nie działa na nich tak, jak można się spodziewać:
template<class T>
void func(T&& param) {
if (std::is_same<T,int>::value)
std::cout << "param is an int\n";
else
std::cout << "param is not an int\n";
}
int main() {
int three = 3;
func(three); //prints "param is not an int"!!!!
}
http://coliru.stacked-crooked.com/a/24476e60bd906bed
Rozwiązaniem jest użycie std::decay
:
template<class T>
void func(T&& param) {
if (std::is_same<typename std::decay<T>::type,int>::value)
std::cout << "param is an int\n";
else
std::cout << "param is not an int\n";
}
decay
jest bardzo agresywny, np. po zastosowaniu do odwołania do tablicy daje wskaźnik. Zazwyczaj jest zbyt agresywny dla tego rodzaju metaprogramowania IMHO.
remove_const_t< remove_reference_t<T> >
ewentualnie zawinę w niestandardową metafunkcję.