Consider this code:
#include <tuple>
#include <type_traits>
#include <iostream>
template <typename T, typename = void> struct is_tuple_like : std::false_type {};
template <typename T> struct is_tuple_like<T, decltype(std::tuple_size_v<T>, void())> : std::true_type {};
int main()
{
std::cout << is_tuple_like<std::string>::value << '\n';
}
On GCC 10.2 and MSVC 19.28 it causes a hard error, along the lines of:
error: incomplete type 'std::tuple_size<...>' used in nested name specifier
On Clang 11.0.1, on the other hand, it compiles and prints 1, with both libstdc++ and libc++.
Which compiler is correct here?
Notice that Clang prints 1 and not 0, meaning it doesn't treat std::tuple_size<std::string>::value (the initializer of tuple_size_v) as a soft error, but instead chooses to disregard it completely!
Which makes sense in a way, since if tuple_size_v is defined as template <typename T> inline constexpr size_t tuple_size_v = ..., the type decltype(tuple_size_v<...>) doesn't depend on the template parameter, and is always size_t.
I guess the question boils down to whether the initializer of tuple_size_v is required to be instantiated here, even though it's not strictly necessary.
I know that I can fix it by replacing std::tuple_size_v<...> with std::tuple_size<...>::value, then it prints 0 on all three compilers.