Во-первых, ваш работает, но зависит от того, является ли он void
.Этот облегченный тег может быть полезен, если в некоторых ситуациях он имеет тип, отличный от void, и если он не является void, вы молча получаете ошибку при обнаружении, что кажется плохим.
Во-вторых, ваша маркировка типа требует Вы можете изменить тип, что означает, что вы не можете получить встроенные или типы, которые вам не принадлежат (например, в std
).Мы можем это исправить.
namespace type_tag {
namespace adl {
template<template<class...>class tag>
struct tag_token_t {};
template<class T, template<class...> class tag>
constexpr
decltype( (void)(std::declval<tag<T>>()), std::true_type{} )
tag_test( T*, tag_token_t<tag> ) { return {}; }
template<class T, template<class...> class tag, class...LowPriority>
constexpr
std::enable_if_t<!std::is_same<T,int>{}, std::false_type> tag_test(T*, tag_token_t<tag>, LowPriority&&...) {
return {};
}
}
template<template<class...>class Z>using tag_token_t = adl::tag_token_t<Z>;
template<template<class...>class tag>
constexpr tag_token_t<tag> tag_token{};
namespace details {
template<class T, template<class...>class tag>
constexpr auto test_impl( T*, tag_token_t<tag> ) {
return tag_test( (T*)nullptr, tag_token<tag> );
}
}
template<class T, template<class>class tag>
constexpr auto tag_test() {
return details::test_impl((T*)nullptr, tag_token<tag>);
}
}
, поэтому теперь есть тег:
template<class T>
using my_tag = typename T::my_tag;
мы можем проверить это следующим образом:
constexpr auto double_has_tag = type_tag::tag_test< double, my_tag >();
, который возвращает компиляцию-time true или false, если double
имеет тег.
Мы можем решить, что int
имеет тег, выполнив:
namespace type_tag::adl {
constexpr std::true_type tag_test( int*, type_tag::tag_token_t<my_tag> ) {
return {};
}
}
или для типов, которые мы контролируем:
struct my_tagged_type {
using my_tag = void;
};
для типов, которые мы можем вводить имена в их пространство имен (т. Е. Не std
или встроенные типы), которые мы можем сделать:
namespace not_my_ns {
constexpr std::true_type tag_test( not_my_type*, ::tag_test::tag_token_t<::my_tag> ) {
return {};
}
}
и внезапно type_tag::tag_test<not_my_ns::not_my_type, ::my_tag>()
- это правда.
Как только у нас будет tag_test< type, tag_name >()
, мы можем использовать обычную std::enable_if
вместо какой-либо пользовательской системы.
Преимущества этой системы включают:
Его можно расширить, не меняя ничего в отношении типа, который вы помечаете.
Его можно расширить с помощью using tag=void;
или using tag=int;
, с которым работает ваша система.
В точке использования SFINAE это просто очередное время компиляции bool
.Таким образом, ваши существующие шаблоны SFINAE работают с ним.
Если вы выберете плохое имя для типа тега в структуре, которую кто-то еще использует по несвязанной причине, tag_test
может переопределить это
Недостатком является то, что для этого потребовалось немного магии.Но в общих случаях использования вы получаете ту же работу, которая требуется для конечных пользователей, что и ваша облегченная система.В более сложных случаях использования это позволяет вам делать то, чего не может сделать ваш легкий.
Live пример .