Создание `std:: get` неплохо сочетается с SFINAE
std::get
, похоже, не имеет отношения к SFINAE, как показано в следующем тестовом примере:
template <class T, class C>
auto foo(C &c) -> decltype(std::get<T>(c)) {
return std::get<T>(c);
}
template <class>
void foo(...) { }
int main() {
std::tuple<int> tuple{42};
foo<int>(tuple); // Works fine
foo<double>(tuple); // Crashes and burns
}
Посмотрите на живое на Coliru
Цель состоит в том, чтобы перевести второй вызов на foo
во вторую перегрузку. На практике libstdС++ дает:
/usr/local/bin/../lib/gcc/x86_64-pc-linux-gnu/6.3.0/../../../../include/c++/6.3.0/tuple:1290:14: fatal error: no matching function for call to '__get_helper2'
{ return std::__get_helper2<_Tp>(__t); }
^~~~~~~~~~~~~~~~~~~~~~~
libС++ более прямой, с прямой детонацией static_assert
:
/usr/include/c++/v1/tuple:801:5: fatal error: static_assert failed "type not found in type list"
static_assert ( value != -1, "type not found in type list" );
^ ~~~~~~~~~~~
Мне бы очень хотелось не внедрять луковые слои, проверяя, является ли C
специализацией std::tuple
, и ищет T
внутри своих параметров...
Есть ли причина, по которой std::get
не будет SFINAE-дружественным? Есть ли лучшее обходное решение, чем то, что описано выше?
Я нашел что-то о std::tuple_element
, но не std::get
.
Ответы
Ответ 1
std::get<T>
явно не является SFINAE-дружественным, как [tuple.elem]:
template <class T, class... Types>
constexpr T& get(tuple<Types...>& t) noexcept;
// and the other like overloads
Требуется: Тип T
встречается ровно один раз в Types...
. В противном случае программа плохо сформирована.
std::get<I>
также явно не относится к SFINAE.
Что касается других вопросов:
Есть ли причина, по которой std::get
не будет SFINAE-дружественным?
Не знаю. Как правило, это не вопрос, который должен быть включен SFINAE. Поэтому я думаю, что это не считалось чем-то, что нужно было сделать. Жесткие ошибки намного легче понять, чем прокручивать кучу нежизнеспособных вариантов кандидата. Если вы считаете, что для std::get<T>
является убедительной причиной для удобства использования SFINAE, вы можете отправить LWG-сообщение об этом.
Есть ли лучшее обходное решение, чем то, что описано выше?
Конечно. Вы можете написать свою собственную SFINAE-дружественную версию get
:
template <class T, class... Types,
std::enable_if_t<(std::is_same<T, Types>::value + ...) == 1, int> = 0>
constexpr T& my_get(tuple<Types...>& t) noexcept {
return std::get<T>(t);
}
А потом сделай так, как хочешь.
Ответ 2
Не используйте SFINAE на std::get
; это не разрешено.
Вот два относительно дружественных способа sfinae, чтобы проверить, можете ли вы using std::get; get<X>(t)
:
template<class T,std::size_t I>
using can_get=std::integral_constant<bool, I<std::tuple_size<T>::value>;
namespace helper{
template<class T, class Tuple>
struct can_get_type:std::false_type{};
template<class T, class...Ts>
struct can_get_type<T,std::tuple<Ts...>>:
std::integral_constant<bool, (std::is_same_v<T,Ts>+...)==1>
{};
}
template<class T,class Tuple>
using can_get=typename helpers::can_get_type<T,Tuple>::type;
Затем ваш код читает:
template <class T, class C, std::enable_if_t<can_get_type<C,T>{},int> =0>
decltype(auto) foo(C &c) {
return std::get<T>(c);
}
Ответ 3
Из N4527 (я полагаю, что он все еще находится в стандарте):
§ 20.4.2.6 (8):
Требуется: Тип T встречается ровно один раз в типах. В противном случае программа плохо сформирована.
Программа, описанная выше, плохо сформирована.
Конец обсуждения.