Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using typename in C++20 requires / concept?

Tags:

c++

c++20

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
like image 877
Andrew Tomazos Avatar asked Feb 15 '21 14:02

Andrew Tomazos


1 Answers

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>.

like image 161
Barry Avatar answered Sep 25 '22 15:09

Barry