Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Concepts: checking for template instantiation

Assuming I have a templated type, e.g.

template<typename A, typename B, typename C>
struct mytype { };

How do I write a concept that checks whether a type is an instantiation of that template?

template<typename T>
concept MyType = requires(T x) { ??? }

I can't figure an obvious way of doing it without resolving to old-style specialised detector types or maybe a marker base type.

like image 973
MrMobster Avatar asked Jan 14 '19 13:01

MrMobster


People also ask

What is a template instantiation?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation.

What happens when a class template is instantiated?

Template instantiation involves generating a concrete class or function (instance) for a particular combination of template arguments. For example, the compiler generates a class for Array<int> and a different class for Array<double>.

How the instantiation of function template happens?

When a function template is first called for each type, the compiler creates an instantiation. Each instantiation is a version of the templated function specialized for the type. This instantiation will be called every time the function is used for the type.

Why is it necessary to instantiate a template?

In order for any code to appear, a template must be instantiated: the template arguments must be provided so that the compiler can generate an actual class (or function, from a function template).


2 Answers

You can define your own meta-function (type trait) for that purpose:

template <typename T>
struct is_mytype : std::false_type { };

template <typename A, typename B, typename C>
struct is_mytype<mytype<A, B, C>> : std::true_type { };

template <typename T>
concept MyType = is_mytype<T>::value;

But to say the truth, I don't know whether there isn't a way how to defining such a concept directly without the need of a separate metafunction.

like image 176
Daniel Langr Avatar answered Sep 28 '22 15:09

Daniel Langr


Using C++17 class template argument deduction, you should be able to do something like this:

template<typename A, typename B, typename C>
struct mytype { };

template<class T>
concept C1 = requires(T x) { 
    { mytype{x} } -> std::same_as<T>;
};

mytype{x} uses class template argument deduction to deduce A, B and C, so this is valid if you can construct a mytype<A, B, C> from a T. In particular, this is valid if mytype is copy-constructible since you have an implicitly declared copy-deduction guide similar to:

template <typename A, typename B, typename C>
mytype(mytype<A, B, C>) -> mytype<A, B, C>;

Checking that T is also the constructed mytype instantiation avoid matching other deduction guides, e.g., this would match for any type without the -> std::same_as<T>:

template <class A, class B, class C>
struct mytype {
    mytype(A);
};

template <class A>
mytype(A) -> mytype<A, A, A>;

The proposed solution does not work for non copy-constructible classes, even though should be possible to make it work for move-only classes.


Tested with clang and gcc: https://godbolt.org/z/ojdcrYqKv

like image 44
Holt Avatar answered Sep 28 '22 15:09

Holt