c++c++-conceptsstdapply

Using std::apply in a concept causes compilation error


I'm trying to test if a function overload exists for a list of types. Arguments for the function are bundled in a tuple, therefore I used std::apply in the testing concept. To my surprise, the concept fails to compile:

#include <concepts>
#include <tuple>
#include <string>

void foo(int) {}

template <class T>
concept OverloadExists = requires(T x) {
    std::apply(
    [](auto const&... args) {
        foo(args...);
    }, x);
};
static_assert(OverloadExists<std::tuple<std::string>> || true);

I was able to achieve the goal by extracting the tuple unpacking outside the concept, but I'm curious though, why this concept doesn't compile?


Solution

  • Neither std::apply nor your lambda are SFINAE friendly, so evaluating your concept leads to hard error instead of soft error (soft error would lead to false result).

    Rewriting your own SFINAE friendly methods would success:

    template<class F, class Tuple, std::size_t... Is>
    constexpr auto apply_impl(F&& f, Tuple&& t, std::index_sequence<Is...>)
    -> decltype(std::invoke(std::forward<F>(f), std::get<Is>(std::forward<Tuple>(t))...))
    {
        return std::invoke(std::forward<F>(f), std::get<Is>(std::forward<Tuple>(t))...);
    }
    
    template<class F, class Tuple>
    constexpr auto apply(F&& f, Tuple&& t)
    -> decltype(apply_impl(std::forward<F>(f),
                           std::forward<Tuple>(t),
                           std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>()))
    {
        return apply_impl(std::forward<F>(f),
                          std::forward<Tuple>(t),
                          std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>());
    }
    
    
    template <class T>
    concept OverloadExists = requires(T x) {
        ::apply(
        [](auto const&... args) -> decltype(foo(args...)){
            foo(args...);
        }, x);
    };
    

    Demo