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?

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • 3
    [CWG 2296](http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#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](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0348r0.html) 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. – Brian Bi Jan 02 '22 at 05:00
  • Better godbolt link: https://godbolt.org/z/MTTGE454j – Marek R Jan 03 '22 at 12:42
  • @MarekR Much better! But note that MSVC compiles but has different behaviour. – Guillaume Racicot Jan 03 '22 at 13:14
  • Now that compilers start to implement concept, why are you trying to make your own implementation? – Phil1970 Jan 03 '22 at 14:31
  • I deleted my answer. To me, the only reasonable solution currently is to delete the second `f` function and work-around classes (`deleted` 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**. – Phil1970 Jan 04 '22 at 18:18
  • @Phil1970 yes it's definitely an edge case and exactly what I was searching for making this code :) – Guillaume Racicot Jan 04 '22 at 19:35

0 Answers0