Please consider the following C++20 program:
#include <iostream>
template<typename T>
struct A {
using X = typename T::X;
};
template<typename T>
constexpr bool WorksWithA = requires { typename A<T>; };
struct GoodArg {
using X = int;
};
struct BadArg {
};
int main() {
std::cout << WorksWithA<GoodArg> << std::endl;
std::cout << WorksWithA<BadArg> << std::endl;
}
Is this ill-formed? And if not, what should the output be?
I was expecting the output to be 1 0
but I observe in clang 1 1
. Who's right and why?
$ clang++ --version
clang version 10.0.0-4ubuntu1
$ clang++ test.cc -std=c++20
$ ./a.out
1
1
The concept here is just naming the type A<BadArg>
, it doesn't do anything to trigger instantiation of it. Nothing here leads to the instantiation of A<BadArg>::X
which would be ill-formed.
If it did though, then you wouldn't get false
anyway, you'd get an ill-formed program. For instance, had you done:
template<typename T>
constexpr bool WorksWithA = requires { A<T>{}; };
Then WorksWithA<BadArg>
would trigger instantiation of A<BadArg>
which would try to look up BadArg::X
, which is now a failure outside of the immediate context of substitution. Not false
, compile error.
If you want a result that's false
, you'll have to constrain the A
template on the type existing:
template <typename T>
requires requires { typename T::X; }
struct A {
using X = typename T::X;
};
And now both formulations (your original and my substitute) would yield false
for WorksWithA<BadArg>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With