A variable template that is true iff a class template would instantiate?

安稳与你 提交于 2021-02-19 05:14:19

问题


Let's say I have a class template A that has one type template parameter, and a single primary specialization:

 template<typename T> struct A {
    /*...*/
 };

For some T arguments A<T> will instantiate successfully, for others it won't.

Without modifying or extending the definition of A, is it possible to write a bool variable template:

template<typename T>
constexpr bool WorksWithA = /*...*/;

such that WorkWithA<T> is true iff A<T> would instantiate successfully?

Update

Posted this as separate question: Using typename in C++20 requires / concept?

#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;
}

Compiled and run with:

$ clang++ --version
clang version 10.0.0-4ubuntu1 
$ clang++ test.cc -std=c++20
$ ./a.out 
1
1

This outputs 1 1, expected output is 1 0 ?

What gives?


回答1:


If I'm understanding this correctly, for SFINAE friendly types, this could be a way:

Match if instantiation is possible:

template<class T>
constexpr auto WorksWithA(int) -> A<T>;

Match if it isn't:

struct no {};
template<class T>
constexpr auto WorksWithA(long) -> no;

Helper:

template<typename T>
inline constexpr bool WorksWithA_v = 
    not std::is_same_v<no, decltype(WorksWithA<T>(0))>;

Demo


If A isn't SFINAE friendly (as the A in the updated question), you'll have to add the check in WorksWithA. In this case:

template<typename T>
struct A {
    using X = typename T::X;
};

template<class T>
constexpr auto WorksWithA(int) -> typename T::X;

struct no {};
template<class T>
constexpr auto WorksWithA(long) -> no;

template<typename T>
inline constexpr bool WorksWithA_v =
    not std::is_same_v<no, decltype(WorksWithA<T>(0))>;

Demo




回答2:


You misunderstand how templates work.

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 {
};

BadArg fundamentally works with A. It isn't bad.

template<typename T>
struct A {
    using X = typename T::X;
};

this does not require that T have a typename X.

This states that if it does have a typename X, then A::X is that typename.

So WorksWithA does test correctly if T can instantiate the template A.

This doesn't help you directly, but understanding that you are asking the wrong question is important, and why your question is the wrong one. Because that leads to the right question.

Either you want A not not instantiate when T does not have a type X, or you want WorksWithA to do a different kind of test.

For example, maybe you want HasAnX:

template<class T>
concept HasAnX = requires { typename T::X; };

then

static_assert(HasAnX<GoodArg>);
static_assert(!HasAnX<BadArg>);

compiles.

However, I'd guess that semantically this doesn't match what you want it to match. You want it to be the A-ness not the X-ness you are measuring.

Then your problem is probably not with WorksWithA, but with A itself. You thought you where adding a requirement that T HasAnX, but you where not.

template<HasAnX T>
struct A {
    using X = typename T::X;
};

that makes A require that T has an X. Now, WorksWithA ... works as you intended.

Live example.




回答3:


This is a good (and right) question, but I think as up until now there is no answer for this question in general. Lets assume the class template A is simply like:

template<typename T>
class A{
public:
    T variable;
}

This template works with most of types but it does not instantiate with T = void for the obvious reason. now lets assume we want to find that out before hand with a SFINAE friendly method.

The best tool to find out if a type is instantiatable is to check whether it has a size, now Lets define a tool similar to std::void_t:

template<size_t...> using void_size_t = void;

now if we define:

template< class, class = void >
struct pre_WorksWithA : std::false_type { };

template< class T >
struct pre_WorksWithA<T, void_size_t<sizeof(A<T>)>> : std::true_type { };

template<typename T>
constexpr bool WorksWithA = pre_WorksWithA<T>::value;

In case of a normal types like int the result is true which it should be. but in case of void neither the WorksWithA nor pre_WorksWithA can instantiate and the result would be an error from the compiler.

So as you can see, up until now, basically there is no feature in C++ to help us know whether a type is instantiate-able or not without giving an error?




回答4:


You can use a simple concept:

template<typename T>
constexpr bool WorksWithA = requires { typename A<T>; };


来源:https://stackoverflow.com/questions/66208977/a-variable-template-that-is-true-iff-a-class-template-would-instantiate

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!