Why don't types with invalid inheritance get SFINAE-out with void_t?
Asked Answered
C

0

7

Suppose we have a current class:

template<typename T>
struct Inheriter : public T {};

Note that its instantiation would be well-formed only if T is a class/struct that is not declared as final.

Then, with the power of std::void_t and SFINAE we may check in compile-time whether the type is well-formed or ill-formed. Let's use it to write our own naive implementation of std::is_final:

template<typename T, typename = void>
struct IsFinal : public std::true_type{};

template<typename T>
struct IsFinal<T, std::void_t<decltype(Inheriter<T>{})>> : public std::false_type{};

For any arbitrary type T compiler is expected to instantiate the most specialized version of IsFinal, and if the substitution would fail, then try to instantiate the least specialized version. And only if all of them are failed, then compiler must spit out an error.

But on Clang 14 and GCC 11.2 (which are the latest at this moment), when we pass some final type as a template parameter to IsFinal, we get a compilation error (https://godbolt.org/z/q8jxP3Thc).

Why doesn't the compiler try to instantiate the least specialized version of IsFinal, and spits out an error straight up? Is this a compiler bug or some tricky part of the C++ language standard?

Capricorn answered 13/4, 2022 at 15:41 Comment(8)
From my point of view the code is well formed. I had also an issue where access of private members in SFINAE context was rejected by gcc. This bug gcc.gnu.org/bugzilla/show_bug.cgi?id=96204 is fixed for gcc 12 now. I checked your code also with trunk but it still complains about the "final". I would tend to say that is a compiler bug... but I am not a language lawyer :-)Hyperacidity
My guess is that this is correct because this isn't a substitution failure. This isn't trying to find that an expression T{} is failing substitution, this is discovering that the type Inheriter<T> is ill-formed. The same would happen if the definition of T fails a static_assert -- which isn't actually a valid substitution-failure -- or if T was an invalid type like void&Germann
Probably the base class is not an "immediate context". Or it's a compiler bug.Kwang
SFINAE is not a way to check whether a piece of code is well-formed.Carrico
@n.1.8e9-where's-my-sharem. yes, but std::void_t isCapricorn
@Human-Compiler: Why in SFINAE context access of a non existing member would not cause a compile error but inheriting from a final class should result in an error? Is accessing a private member or an non existing class member not ill-formed? In none of the cases a compile error is expected. That is why we have SFINAE and all the enable_if stuff.Hyperacidity
@Hyperacidity SFINAE only applies to substitution errors in the immediate context. This wouldn't appear to be in the immediate context. What is the argument that it is?Anhedral
Related #15261185Carrico

© 2022 - 2024 — McMap. All rights reserved.