std::void_t
| Определено в заголовочном файле <type_traits>
|
||
template< class... > using void_t = void; |
(начиная с C++17) | |
Служебная метафункция, которая отображает последовательность любых типов в тип void. Эта метафункция представляет собой удобный способ использовать SFINAE до концептов C++20, в частности, для условного удаления функций из набора кандидатов в зависимости от того, допустимо ли выражение в неопределённом контексте (например, операнд для выражения decltype), что позволяет существовать отдельным перегрузкам функций или специализациям на основе поддерживаемых операций.
Примечание
Эта метафункция используется в метапрограммировании шаблонов для обнаружения некорректных типов в контексте SFINAE:
// первичный шаблон обрабатывает типы, которые не имеют вложенного элемента ::type:
template< class, class = void >
struct has_type_member : std::false_type { };
// специализация распознаёт типы, которые имеют вложенный элемент ::type:
template< class T >
struct has_type_member<T, std::void_t<typename T::type>> : std::true_type { };
Её также можно использовать для определения достоверности выражения:
// основной шаблон обрабатывает типы, которые не поддерживают пре-инкремент:
template< class, class = void >
struct has_pre_increment_member : std::false_type { };
// специализация распознаёт типы, которые поддерживают пре-инкремент:
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>
// Шаблон переменной, который проверяет, есть ли у типа функции-элементы begin() и end()
template <typename, typename = void>
constexpr bool is_iterable{};
template <typename T>
constexpr bool is_iterable<
T,
std::void_t< decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())
>
> = true;
// Свойство итератора, у которого value_type является value_type итерируемого контейнера,
// поддерживает даже back_insert_iterator (где value_type имеет значение 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_iterable<std::vector<double>>);
SHOW(is_iterable<std::map<int, double>>);
SHOW(is_iterable<double>);
SHOW(is_iterable<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_iterable<std::vector<double>> == true
is_iterable<std::map<int, double>> == true
is_iterable<double> == false
is_iterable<A> == false
Смотрите также
(C++11) |
условно удаляет перегрузку функции или специализацию шаблона из разрешения перегрузки (шаблон класса) |