Making `std::get` play nice with SFINAE

std::get<T> is explicitly not SFINAE-friendly, as per [tuple.elem]:

template <class T, class... Types>
  constexpr T& get(tuple<Types...>& t) noexcept;
// and the other like overloads

Requires: The type T occurs exactly once in Types.... Otherwise, the program is ill-formed.

std::get<I> is also explicitly not SFINAE-friendly.


As far as the other questions:

Is there a reason for std::get not to be SFINAE-friendly?

Don't know. Typically, this isn't a point that needs to be SFINAE-ed on. So I guess it wasn't considered something that needed to be done. Hard errors are a lot easier to understand than scrolling through a bunch of non-viable candidate options. If you believe there to be compelling reason for std::get<T> to be SFINAE-friendly, you could submit an LWG issue about it.

Is there a better workaround than what is outlined above?

Sure. You could write your own SFINAE-friendly verison of get (please note, it uses C++17 fold expression):

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);
}

And then do with that as you wish.


Don't SFINAE on std::get; that is not permitted.

Here are two relatively sfinae friendly ways to test if you can 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;

Then your code reads:

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);
}

From N4527 (I presume it's still in the standard):

§ 20.4.2.6 (8):

Requires: The type T occurs exactly once in Types.... Otherwise, the program is ill-formed.

The program above is ill-formed, according to the standard.

End of discussion.