Implementation divergence about sfinae on default parameter value

Viewed 116

I produced a code that has implementation divergence and I want to know which compiler is right and why, or which part of the standard allows those compiler to differ:

#include <type_traits>
#include <cstdio>

// Only deleted when the constraint is not met
template<typename T>
struct deleted {
    template<typename U = T> requires (not std::is_integral_v<U>)
    deleted() = delete;

    template<typename U = T> requires std::is_integral_v<U>
    deleted() {}
};

struct trigger_error {
    template<typename F>
    operator F () {
        static_assert(not std::is_same_v<F, F>, "Cannot call f with floating point types");
        return F{};
    }
};

// Constrained function. Only callabale with integral types
// When called with something other than integral, display static assert.
// sfinae still applies even though the static assert is called.
template<typename T> requires std::is_integral_v<T>
void f(T) {}

template<typename T> requires (not std::is_integral_v<T>)
auto f(T, int = trigger_error{}, deleted<T> = {}) {};

// Do sfinae if f is callable. If callable, prints "integrals"
template<typename T> requires(requires(T t){f(t);})
auto use_constrains(T) -> void {
    puts("integrals");
}

// Sfinae fails, print "floats" instead
template<typename T>
auto use_constrains(T) -> void {
    puts("floats");
}

int main() {
    use_constrains(1);
    //f(1.3); // uncommenting triggers static assert
    use_constrains(1.3);
}

Live on compiler explorer

To my surprise, the code don't even compiled on Clang and do an hard error on the call to deleted function deleted::deleted().

MSVC compiles, but don't do sfinae, and don't do hard error and simply prints "integrals" on both calls.

Since the three implementations diverges, I wondered who is right and why. Is sfinae applied on the expression of the default value of a parameter?

0 Answers
Related