Typeigenschaften
Ein Typattribut definiert eine vorlagenbasierte Struktur zur Kompilierungszeit zum Abfragen oder Ändern der Eigenschaften eines Typs.
Der Versuch, eine im Header <type_traits> definierte Vorlage zu spezialisieren, führt zu undefiniertem Verhalten, mit der Ausnahme, dass std::common_type wie beschrieben spezialisiert werden kann.
In der Header-Datei <type_traits> definierte Vorlagen können mit unvollständigen Typen instanziiert werden, sofern nicht anders angegeben, obwohl die Instanziierung von Standardbibliotheksvorlagen mit unvollständigen Typen generell verboten ist.
Verschiedene Transformationen
Leitet den Ergebnistyp des Aufrufs eines aufrufbaren Objekts mit einer Reihe von Argumenten ab
std::result_of,
std::invoke_result
Vorlage< Klasse > class result_of; // 不定义 |
(1) | (C++11-Ursprung) (C++17-Zwischenverwendung) (C++20-Zwischenentfernung) |
Vorlage< Klasse F, Klasse... ArgTypes> |
(2) | (seit C++17) |
Der Rückgabetyp eines INVOKE-Ausdrucks wird zur Kompilierungszeit abgeleitet.
F muss ein aufrufbarer Typ, ein Verweis auf eine Funktion oder ein Verweis auf einen aufrufbaren Typ sein. Der Aufruf von mit ArgTypes... muss ein wohlgeformter Ausdruck sein. F |
(seit C++11) |
F Alle Typen in und ArgTypes können ein beliebiger vollständiger Typ, ein Array mit unbekannten Grenzen oder (kann cv-qualifiziert sein) seinvoid |
(seit C++14) |
Mitgliedstyp
Mitgliedstyp | Definition |
type |
Bei Aufruf mit ParameternAufrufbar (Aufrufbarer) Typ ist der Rückgabetyp. F ist nur definiert, wenn es in einem nicht ausgewerteten Kontext mit den Argumenten ArgTypes... aufgerufen werden kann. (seit C++14)ArgTypes... F |
Hilfstyp
Vorlage< Klasse T > |
(1) | (C++14-Ursprung) (C++17-Zwischenverwendung) (C++20-Zwischenentfernung) |
Vorlage< Klasse F, Klasse... ArgTypes> |
(2) | (seit C++17) |
mögliche Umsetzung
namespace detail {
template <class T>
struct is_reference_wrapper : std::false_type {};
template <class U>
struct is_reference_wrapper<std::reference_wrapper<U>> : std::true_type {};
template <class Base, class T, class Derived, class... Args>
auto INVOKE(T Base::*pmf, Derived&& ref, Args&&... args)
-> typename std::enable_if<std::is_function<T>::value &&
std::is_base_of<Base, typename std::decay<Derived>::type>::value,
decltype((std::forward<Derived>(ref).*pmf)(std::forward<Args>(args)...))>::type;
template <class Base, class T, class RefWrap, class... Args>
auto INVOKE(T Base::*pmf, RefWrap&& ref, Args&&... args)
-> typename std::enable_if<std::is_function<T>::value &&
is_reference_wrapper<typename std::decay<RefWrap>::type>::value,
decltype((ref.get().*pmf)(std::forward<Args>(args)...))>::type;
template <class Base, class T, class Pointer, class... Args>
auto INVOKE(T Base::*pmf, Pointer&& ptr, Args&&... args)
-> typename std::enable_if<std::is_function<T>::value &&
!is_reference_wrapper<typename std::decay<Pointer>::type>::value &&
!std::is_base_of<Base, typename std::decay<Pointer>::type>::value,
decltype(((*std::forward<Pointer>(ptr)).*pmf)(std::forward<Args>(args)...))>::type;
template <class Base, class T, class Derived>
auto INVOKE(T Base::*pmd, Derived&& ref)
-> typename std::enable_if<!std::is_function<T>::value &&
std::is_base_of<Base, typename std::decay<Derived>::type>::value,
decltype(std::forward<Derived>(ref).*pmd)>::type;
template <class Base, class T, class RefWrap>
auto INVOKE(T Base::*pmd, RefWrap&& ref)
-> typename std::enable_if<!std::is_function<T>::value &&
is_reference_wrapper<typename std::decay<RefWrap>::type>::value,
decltype(ref.get().*pmd)>::type;
template <class Base, class T, class Pointer>
auto INVOKE(T Base::*pmd, Pointer&& ptr)
-> typename std::enable_if<!std::is_function<T>::value &&
!is_reference_wrapper<typename std::decay<Pointer>::type>::value &&
!std::is_base_of<Base, typename std::decay<Pointer>::type>::value,
decltype((*std::forward<Pointer>(ptr)).*pmd)>::type;
template <class F, class... Args>
auto INVOKE(F&& f, Args&&... args)
-> typename std::enable_if<!std::is_member_pointer<typename std::decay<F>::type>::value,
decltype(std::forward<F>(f)(std::forward<Args>(args)...))>::type;
} // namespace detail
// 最小 C++11 实现:
template <class> struct result_of;
template <class F, class... ArgTypes>
struct result_of<F(ArgTypes...)> {
using type = decltype(detail::INVOKE(std::declval<F>(), std::declval<ArgTypes>()...));
};
// 符合 C++14 的实现(亦为合法的 C++11 实现):
namespace detail {
template <typename AlwaysVoid, typename, typename...>
struct invoke_result { };
template <typename F, typename...Args>
struct invoke_result<decltype(void(detail::INVOKE(std::declval<F>(), std::declval<Args>()...))),
F, Args...> {
using type = decltype(detail::INVOKE(std::declval<F>(), std::declval<Args>()...));
};
} // namespace detail
template <class> struct result_of;
template <class F, class... ArgTypes>
struct result_of<F(ArgTypes...)> : detail::invoke_result<void, F, ArgTypes...> {};
template <class F, class... ArgTypes>
struct invoke_result : detail::invoke_result<void, F, ArgTypes...> {};
Ausgabe
#include <type_traits>
#include <iostream>
struct S
{
double operator()(char, int&);
float operator()(int)
{
return 1.0;
}
};
template<class T>
typename std::result_of<T(int)>::type f(T& t)
{
std::cout << "overload of f for callable T" << std::endl;
return t(0);
}
template<class T, class U>
int f(U u)
{
std::cout << "overload of f for non-callable T" << std::endl;
return u;
}
int main()
{
std::cout << std::boolalpha;
// 以 char 和 int 参数调用 S 的结果是 double
std::result_of<S(char, int&)>::type d = 3.14; // d 拥有 double 类型
std::cout << "std::is_same<decltype(d), double>::value: "
<< std::is_same<decltype(d), double>::value << std::endl;
// 以 int 参数调用 S 的结果是 float
std::result_of<S(int)>::type x = 3.14; // x 拥有 float 类型
std::cout << "std::is_same<decltype(x), double>::value: "
<< std::is_same<decltype(x), double>::value << std::endl;
// result_of 能以指向成员函数的指针以如下方式使用
struct C
{
double Func(char, int&);
};
std::result_of<decltype(&C::Func)(C, char, int&)>::type g = 3.14;
std::cout << "std::is_same<decltype(g), double>::value: "
<< std::is_same<decltype(g), double>::value << std::endl;
f<C>(1); // C++11 中可能编译失败; C++14 中调用不可调用重载
return 0;
}