Template candidate ignored despite a user provided deduction guide

137 Views Asked by At

I have a struct sequence similar to std::integer_sequence, but it can hold values of multiple types as non-type template parameters (sort of like a compile-time tuple). It is implicitly constructible from std::integer_sequence by using a constexpr constructor and a user-defined deduction guide. I also have a function apply_constexpr that applies a non-type template parameter pack to a function, similarly to std::apply. Here they are:

template <auto...>
struct sequence {
    template <typename T, T... Ts>
    constexpr sequence(std::integer_sequence<T, Ts...>) noexcept {
    }
};

template <typename T, T... Ts>
sequence(std::integer_sequence<T, Ts...>) -> sequence<Ts...>;

template<typename Fn, auto... Vals>
constexpr std::invoke_result_t<Fn, decltype(Vals)...>
apply_constexpr(Fn&& fn, sequence<Vals...>)
noexcept(std::is_nothrow_invocable_v<Fn, decltype(Vals)...>) {
    return fn(std::forward<decltype(Vals)>(Vals)...);
}

They can be used like this:

static_assert(apply_constexpr(
    [&](auto&&... i) { return ((f(i) == g(i)) && ...); },
    sequence{ std::make_index_sequence<100>() })
);

When the sequence is constructed explicitly like above, everything is fine. However because the constructor is not explicit the following also works:

sequence s = std::make_index_sequence<100>();

But despite it working, the following does not work (candidate template ignored: could not match 'sequence' against 'integer_sequence'):

static_assert(apply_constexpr(
    [&](auto&&... i) { return ((f(i) == g(i)) && ...); },
    std::make_index_sequence<100>())
// no "sequence{ ... }" here, trying to rely on implicit conversion
);

Why doesn't it work and what could I do to make it work?

1

There are 1 best solutions below

0
On BEST ANSWER

You could make an overload that forward to your first apply_constexpr function. Something like this.

template<typename Fn, typename Is>
constexpr auto apply_constexpr(Fn&& fn, Is s)
noexcept(noexcept(apply_constexpr(fn, sequence{s}))) {
    return apply_constexpr(std::forward<Fn>(fn), sequence{s});
}

The reason it doesn't work in the first place is that template deduction is always on the exact type. When deducing the compiler will not consider conversions. If you pass a std::integer_sequence, that's what the compiler will deduce.

Doing conversions and deductions in one step is not supported by the language.