Reputation: 1972
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.
Upvotes: 17
Views: 4928
Reputation: 37606
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
Upvotes: 13
Reputation: 157314
In the interests of terseness:
template<typename T>
concept MyType = requires(T** x) {
[]<typename A, typename B, typename C>(mytype<A, B, C>**){}(x);
};
The double-pointer is necessary to avoid derived-to-base conversions, ex. struct S : mytype<int, int, int> {}
.
This doesn't work in clang at present, since it doesn't allow lambdas in unevaluated context. You can workaround by providing a helper variable template:
template<class T> constexpr auto L = []<typename A, typename B, typename C>(mytype<A, B, C>**){};
template<typename T>
concept MyType = requires(T** x) { L<T>(x); };
As long as mytype
's template arguments are all types, you can make this even terser using placeholders:
template<typename T>
concept MyType = requires(T** x) { [](mytype<auto, auto, auto>**){}(x); };
At present, this only works in gcc.
Upvotes: 3
Reputation: 1
If you give your template class some traits you can do the following:
template<typename A, typename B, typename C>
struct mytype {
using a_type = A;
using b_type = B;
using c_type = C;
};
With the associate concept:
template <typename T>
concept is_mytype =
std::is_same_v<
std::remove_const_t<T>,
mytype<typename T::a_type, typename T::b_type, typename T::c_type>>;
Or, if mytype has members of those types you can skip the traits:
template<typename A, typename B, typename C>
struct mytype {
A a_inst;
B b_inst;
C c_inst;
};
Giving the concept:
template <typename T>
concept is_mytype =
std::is_same_v<
std::remove_const_t<T>,
mytype<decltype(T::a_inst), decltype(T::b_inst), decltype(T::c_inst)>>;
Upvotes: 0
Reputation: 23497
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.
Upvotes: 12
Reputation: 302643
You can write a generalized trait to check for specializations:
template <typename T, template <typename...> class Z>
struct is_specialization_of : std::false_type {};
template <typename... Args, template <typename...> class Z>
struct is_specialization_of<Z<Args...>, Z> : std::true_type {};
template <typename T, template <typename...> class Z>
inline constexpr bool is_specialization_of_v = is_specialization_of<T,Z>::value;
Which you can make into either a generalized concept:
template<typename T, template <typename...> class Z>
concept Specializes = is_specialization_of_v<T, Z>;
template<typename T>
concept MyType = Specializes<T, mytype>;
or just a specialized one:
template<typename T>
concept MyType = is_specialization_of_v<T, mytype>;
Upvotes: 4