std::void_t
Определён в заголовочном файле <type_traits> | ||
|---|---|---|
template< class... > using void_t = void; | (начиная с C++17) |
Вспомогательная метафункция, преобразующая последовательность типов в тип void. Эта метафункция позволяет удобно использовать SFINAE до появления в C++20 концептов, в частности для условного удаления функций из набора кандидатов в зависимости от того, допустимо ли выражение в невычисляемом контексте (например, в качестве операнда выражения decltype), что позволяет создавать отдельные перегрузки функций или специализации для поддерживаемых операций.
Примечания
Эта метафункция используется в метапрограммировании шаблонов для обнаружения некорректных типов в контексте SFINAE:
// primary template handles types that have no nested ::type member:
template<class, class = void>
struct has_type_member : std::false_type {};
// specialization recognizes types that do have a nested ::type member:
template<class T>
struct has_type_member<T, std::void_t<typename T::type>> : std::true_type {};
Её также можно использовать для проверки допустимости выражения:
// primary template handles types that do not support pre-increment:
template<class, class = void>
struct has_pre_increment_member : std::false_type {};
// specialization recognizes types that do support pre-increment:
template<class T>
struct has_pre_increment_member<T,
std::void_t<decltype(++std::declval<T&>())>
> : std::true_type {};
До принятия решения по вопросу CWG 1558 (дефект C++11) неиспользуемые параметры в шаблонах псевдонимов не гарантировали срабатывание SFINAE и могли игнорироваться, поэтому для более старых компиляторов требуется более сложное определение void_t, например
template<typename... Ts>
struct make_void { typedef void type; };
template<typename... Ts>
using void_t = typename make_void<Ts...>::type;
| Макрос для проверки поддержки функциональности | Значение | Стандарт | Функциональность |
|---|---|---|---|
__cpp_lib_void_t | 201411L | (C++17) |
std::void_t |
Пример
#include <iomanip>
#include <iostream>
#include <map>
#include <type_traits>
#include <vector>
// Variable template that checks if a type has begin() and end() member functions
template<typename, typename = void>
constexpr bool is_range = false;
template<typename T>
constexpr bool is_range<T,
std::void_t<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>> = true;
// An iterator trait those value_type is the value_type of the iterated container,
// supports even back_insert_iterator (where value_type is void)
template<typename T, typename = void>
struct iterator_trait : std::iterator_traits<T> {};
template<typename T>
struct iterator_trait<T, std::void_t<typename T::container_type>>
: std::iterator_traits<typename T::container_type::iterator> {};
class A {};
#define SHOW(...) std::cout << std::setw(34) << #__VA_ARGS__ \
<< " == " << __VA_ARGS__ << '\n'
int main()
{
std::cout << std::boolalpha << std::left;
SHOW(is_range<std::vector<double>>);
SHOW(is_range<std::map<int, double>>);
SHOW(is_range<double>);
SHOW(is_range<A>);
using container_t = std::vector<int>;
container_t v;
static_assert(std::is_same_v<
container_t::value_type,
iterator_trait<decltype(std::begin(v))>::value_type>);
static_assert(std::is_same_v<
container_t::value_type,
iterator_trait<decltype(std::back_inserter(v))>::value_type>);
}
Вывод:
is_range<std::vector<double>> == true is_range<std::map<int, double>> == true is_range<double> == false is_range<A> == false
См. также
|
(C++11) | условно удаляет перегрузку функции или специализацию шаблона из разрешения перегрузки (шаблон класса) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/types/void_t