Is function trailing return type evaluated when requires clause fails
Asked Answered
N

1

6

Simple code as below or as on godbolt doesn't compile with clang but compiles fine with gcc and Visual Studio.

The trailing return type decltype(foo(t)) of baz(T t) is not evaluated when SFINAE fails with clang, gcc and Visual Studio.

However, the trailing return type decltype(foo(t)) of bar(T t) is still evaluated when requires clause fails with clang. The trailing return type is not evaluated when requires clause fails with gcc and Visual Studio. Which compiler is correct on this?

Thank you.

#include <utility>
#include <type_traits>

template<typename T>
auto foo(T) {
    static_assert(std::is_integral<T>::value); // clang error: static_assert failed due to requirement 'std::is_integral<double>::value'
    return 5;
}
 
template<typename T> requires std::is_integral<T>::value
auto bar(T t) -> decltype(foo(t)) {
    return foo(t);
}

template<typename T> requires (!std::is_integral<T>::value)
auto bar(T) {
    return 1.5;
}

template<typename T, std::enable_if_t<std::is_integral<T>::value>* = nullptr>
auto baz(T t) -> decltype(foo(t)) {
    return foo(t);
}

template<typename T, std::enable_if_t<!std::is_integral<T>::value>* = nullptr>
auto baz(T) {
    return 1.5;
}

int main()
{
    bar(5.); // fails with clang, works with gcc and VS
    baz(5.); // works with clang, gcc and VS
}
Neckcloth answered 31/5, 2022 at 16:59 Comment(2)
FWIW, trailing return types don't appear to have anything to do with this: godbolt.org/z/jW5bE3qsGClea
@Frank Yes, decltype triggers the error. It compiles fine with just auto.Neckcloth
T
9

This is CWG 2369, which clang does not appear to implement yet.

The example in that issue (which can now be found in [temp.deduct.general]/5) doesn't compile on clang, but does on gcc.

template <class T> struct Z {
  typedef typename T::x xx;
};
template <class T> concept C = requires { typename T::A; };
template <C T> typename Z<T>::xx f(void *, T); // #1
template <class T> void f(int, T);             // #2

struct A {} a;

struct ZZ {
  template <class T, class = typename Z<T>::xx> operator T *();
  operator int();
};

int main() {
  ZZ zz;
  f(1, a);   // OK, deduction fails for #1 because there is no conversion from int to void*
  f(zz, 42); // OK, deduction fails for #1 because C<int> is not satisfied
}
Tap answered 31/5, 2022 at 17:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.