Implementation divergence about sfinae on default parameter value
Asked Answered
L

0

7

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?

Lalita answered 2/1, 2022 at 4:37 Comment(6)
CWG 2296 is about basically this exact issue, but I don't understand why the submitter claimed that "the current specification makes this example ill-formed". The authors of P0348 seemed to think that the standard was ambiguous on this issue. Maybe someone who is on the committee can provide an answer as to what CWG actually concluded about this issue.Cherub
Better godbolt link: godbolt.org/z/MTTGE454jSainthood
@MarekR Much better! But note that MSVC compiles but has different behaviour.Lalita
Now that compilers start to implement concept, why are you trying to make your own implementation?Pamella
I deleted my answer. To me, the only reasonable solution currently is to delete the second f function and work-around classes (deleted<T> and trigger_error). If the function is really deleted (with non integer types), testing its existance is easy and a bunch of if constexpr can do a lot of things... Having said that, I think that those edge cases are not well defined otherwise compiler would probably agree.Pamella
@Pamella yes it's definitely an edge case and exactly what I was searching for making this code :)Lalita

© 2022 - 2024 — McMap. All rights reserved.