This doesn't compile:
std::get<char>(std::tuple<int, double>{});
However this evaluates to true
.
template<typename T, typename V>
concept HasV = requires () {
std::get<char>(std::tuple<int, double>{});
};
It was my understanding that concepts like the one above check whether the code is compilable and evaluate to false
if it's not. However it doesn't seem to be the case here.
Live demo here: https://godbolt.org/z/9rfrKo7aa
When checking the validity of a requirement only the so-called immediate context of the expression is checked.
The immediate context does not include any verification of whether instantiations that would be necessary if the call is actually evaluated would be well-formed.
In this case, std::get
overloads for the std::tuple
are not specified to be excluded from overload resolution if the template argument given to std::get
is invalid. Therefore overload resolution will work fine and select the std::get<char>
overload. If it isn't defined as deleted, then it can be called just fine. As you can see on cppreference it just says that the call shall be ill-formed if the template argument is incorrect.
One way that an implementation can make a call ill-formed, rather than excluding it from overload resolution, is to add a static_assert
to its body to check the condition. Then, if the call actually appears as a (potentially-)evaluated expression, std::get<char>
will be implicitly instantiated and in that instantiation the static_assert
will fail, making the instantiation and with it the program ill-formed. However, the ill-formedness is in the instantiation of std::get<char>
, which is not in the immediate context of the expression in the requirement.
This is exactly the same thing that would happen if you tried to do SFINAE on the validity of the std::get
template argument. It would never fail either.
A common term used for this is "SFINAE-friendliness". If std::get
for std::tuple
was SFINAE-friendly, then it would instead be specified so that the overloads are excluded from overload resolution when the template argument is invalid for the tuple and then the implementation would be required to e.g. add constraints on std::get
so that overload resolution will not consider the overload viable in that case. Then, the call expression itself will be ill-formed and your requirement would be able to detect it.