C++ Concepts: checking for template instantiation - c++

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.

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

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.

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

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.

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)>>;

Related

Partial template deduction in template argument list

I have the following structs:
template<typename T1, typename T2>
struct A {};
template<typename T>
struct B {};
template<typename A, typename B>
struct C {};
and want to use them as follows:
C<B<int>, A<double, B<int>>> c;
Is there any way to deduce the second template parameter for A, such that I can use it like this?
C<B<int>, A<double>> c;
This should work for any template arguments of C and not just for a particular one (therefore default arguments don't seem to work).
Furthermore a solution for variadic templates would be even better, so instead of
C<B<int>, A<double, B<int>>, A<float, A<double, B<int>>>> c;
something like this would be nice:
C<B<int>, A<double>, A<float>> c;
With a bit of template metaprogramming fun, I was able to solve your problem both for the two-parameter and the variadic case (although in a somewhat narrow way):
// TypeList stuff
template<class... Args>
struct List {
template<class Arg>
using Add = List<Args..., Arg>;
};
template<class List> struct TailI;
template<class A, class... Ar> struct TailI<List<A, Ar...>> {
using type = typename TailI<List<Ar...>>::type;
};
template<class A> struct TailI<List<A>> {
using type = A;
};
template<class List>
using Tail = typename TailI<List>::type;
template<template<class...> class OP, class List> struct rename_impl;
template<template<class...> class OP, class... ListArgs>
struct rename_impl<OP, List<ListArgs...>> {
using type = OP<ListArgs...>;
};
template<template<class...> class OP, class List>
using rename = typename rename_impl<OP, List>::type;
// Actual code solving problem at hand
template<class T1, class T2> struct A{};
template<class... Args> struct C{};
template<class Built, class Next, class... Rest>
struct builder {
using NewBuilt = typename Built::template Add<A<Next, Tail<Built>>>;
using type = typename builder<NewBuilt, Rest...>::type;
};
template<class Built, class Next>
struct builder<Built, Next> {
using NewBuilt = typename Built::template Add<A<Next, Tail<Built>>>;
using type = rename<C, NewBuilt>;
};
template<class First, class... Rest>
using c_builder = typename builder<List<First>, Rest...>::type;
using t = c_builder<int, double, float>;
// Test driver
#include <utility>
static_assert(std::is_same_v<t, C<int, A<double, int>, A<float, A<double, int>>>>, "");
For the simpler case of
template<typename A, typename B> struct C {};
you can use a helper class to provide you the type you need.
template<typename T1, typename T2>
struct C_Helper
{
using type = C<B<T1>, A<T2, B<T1>>>;
};
and use
using C_Type = typename C_Helper<int, double>::type;
C_Type c;
I haven't thought through how that can be extended to support a variadic class template C,

Obtain original struct/class name during C++ template instantiation

template<typename T> struct S {};
template<typename T> struct R {};
int main() {
typedef S<double> s1;
typedef S<int> s2;
typedef R<int> s3;
static_assert(xxx<s1, s2>::value,
"No, assertion must not be raised");
static_assert(xxx<s2, s3>::value,
"Yes, assertion must be raised");
}
So, I want xxx<s1, s2>::value to return true while xxx<s2, s3>::value to return false during compile-time.
Is the existence of xxx impossible in C++?
Or, is the existence of xxx theoretically possible in C++ but possibly no one has done it yet?
Use two specialisations​ that use template template parameters to perform this "matching":
template<
typename T,
typename V>
struct xxx;
template<
template <class> class A,
template <class> class B,
typename X,
typename Y>
struct xxx<A<X>, B<Y>> {
static constexpr const int value = false;
};
template<
template <class> class U,
typename X,
typename Y>
struct xxx<U<X>, U<Y>> {
static constexpr const int value = true;
};
With your code on ideone
Note: For it to be a real type trait you should not set value manually, but derive from std::integral_constant (std::true_type or std::false_type). Above is just a quick mockup I did on my phone.
Something like same_base_template:
#include <type_traits>
template<class A, class B>
struct same_base_template : std::false_type{};
template<template<class...> class S, class... U, class... V>
struct same_base_template<S<U...>, S<V...>> : std::true_type{};
Edit:
And a third specialization since you are using non-type template arguments (std::ratio):
template<class T, template<T...> class S, T... U, T... V>
struct same_base_template<S<U...>, S<V...>> : std::true_type{};
Demo
This uses true_typeand false_type from type_traits so we don't need to write a constexpr bool value ourselves. I used a variadic template here because it was slightly more generic and took only a few more keystrokes to do. For your specific use case, you don't need them)

Can a template template be specialized for fundamental types like regular templates?

I have written the following code to infix a templated class:
template<template<typename...> class C, typename A, typename B>
struct infix_t
{
typedef C<A, B> type;
};
template<template<typename...> class C, class A>
constexpr infix_t<C, A> operator<(A, infix_t<C>)
{
return {};
}
template<template<typename...> class C, typename A, typename B>
constexpr C<A, B> operator>(infix_t<C, A>, B)
{
return {};
}
This allows me to write a <same_as> b where same_as has type infix_t<std::is_same>. However, if the type of a or b is a fundamental type, I get template template argument has different template parameters...; if I try to redeclare infix_t to accept fundamental types, I get template argument ... must be a class template or alias template. I thought that templates over typenames could accept fundamentals anyway. Is this because it's a template template, and the rules are different, or am I just approaching this wrong?
To make your code compile, it is pretty easy:
template<template<class...> class C, class...Z>
struct infix_t {};
template<template<class...> class C, class A>
constexpr infix_t<C, A> operator<(A, infix_t<C>)
{ return {}; }
template<template<class...> class C, class A, class B>
constexpr C<A, B> operator>(infix_t<C, A>, B)
{ return {}; }
live example.
If you want to take std::integral_constant and apply this, you first wrap it up:
template<class T, class U>
using bob = std::integral_constant<T, U{}>;
now bob is an integral_constant that only takes types. It expects the second type to encode the value of the constant, and the first the type.
This is turtles all the way down, in that U is probably itself going to be an integral constant.
There is no way to take the value 7 and generate an integral constant from an expression. You can take the token 7_constant and generate std::integral_constant<int, 7>, but that is a different problem.
To expand and modify the solution by #Yakk a bit, here's a version that allows compile-time comparison of types
#include <type_traits>
template<template<class...> class Op, class... Args>
struct infix {};
template<template<class...> class Op, class L>
constexpr infix<Op, L> operator<(L, infix<Op>)
{ return {}; }
template<template<class...> class Op, class L, class R>
constexpr Op<L, R> operator>(infix<Op, L>, R)
{ return {}; }
constexpr auto same_as = infix<std::is_same>{};
template<int N>
constexpr auto int_c = std::integral_constant<int, N>::value;
int main()
{
static_assert( int_c<0> <same_as> int_c<0> );
static_assert( !(int{} <same_as> char{}) );
}
Live Example

Type trait to check whether some type is derived from a class template

Please consider the following code snippet:
template<class A, class B>
class c {};
template<class D>
class e
: public c<e<D>, /* some type depending on D */>
{ }
Given a type F, how can I check whether there is some type B such that F is derived from c<F, B>?
Example: For F = e<D> there is some type B depending on D such that F is derived from c<F, B>.
This answer focuses on the question;
Given a type F, how can I check whether there is some type B such that F is derived from c<F, B>?
And the comment for clarity;
The trait should check if F is derived from c<F, B> for some B (and it's not important what B is).
Two constexpr functions can be used to "attract" and differentiate the base c<F, B> away from other types. Function templates are favoured because they are able to deduce types (this would be required to satisfy the requirement some B). Something of the form as follows...
template <typename F, typename B>
constexpr bool check_base(C<F, B> &&) { return true; }
template <typename F>
constexpr bool check_base(...) { return false; }
The following sample, with improved usage scenarios, illustrates the basic workings;
#include <utility>
template <typename A, typename B>
struct C {};
template <typename F, typename B>
constexpr std::true_type check_base_(C<F, B>&&) { return {}; }
template <typename F>
constexpr std::false_type check_base_(...) { return {}; }
template <typename T>
using check_base = decltype(check_base_<T>(std::declval<T>()));
template <typename D>
struct E : C<E<D>, D> {};
struct FailF {};
int main()
{
static_assert(check_base<E<int>>());
static_assert(!check_base<FailF>());
}
See the demo here.
If we remove the constexpr, we can also remove the unneeded inline definitions of the check_base_ functions.
Note: the solutions assumes/asserts an accessible base class (i.e. not private or protected). If the base class is private, the code above would fail to compile, with an accessibility error. The code below will not fail, the SFINAE is done to allow the compilation to continue.
Online demo here.
#include <utility>
#include <type_traits>
template <typename A, typename B>
struct C {};
template <typename F, typename B>
std::true_type check_base_(C<F, B>&&, typename std::enable_if<std::is_convertible<F, C<F,B>>::value>::type* = nullptr);
template <typename F>
std::false_type check_base_(...);
template <typename T>
using check_base = decltype(check_base_<T>(std::declval<T>()));
template <typename D>
struct Example : C<Example<D>, D> {};
struct CtorTest : C<CtorTest, int> { CtorTest(int, int) {} };
struct PrivateBase : private C<PrivateBase, double> {};
struct FailTest {};
int main()
{
static_assert(check_base<Example<int>>(), "fail...");
static_assert(check_base<CtorTest>::value, "fail...");
static_assert(!check_base<PrivateBase>(), "fail...");
static_assert(!check_base<FailTest>(), "fail...");
}

Template template and partial specialization: a puzzle

Consider the following code:
template<typename>
struct S { };
template<typename, typename>
struct B;
template <typename R, typename... Args, template<class> class C>
struct B<R(Args...), C<R>> {
void f() { }
};
int main() {
B<void(), S<void>> b;
b.f();
}
It compiles and has no problem.
Anyway, whenever one decides to use B, it has to provide two types.
What I'd like to achieve is to default somehow the second parameter (I know, partial specializations do not accept a default for their parameters) and let an user define it's type as B<void()> instead of B<void(), S<void>>.
Unfortunately, because of template template, partial specialization and the dependency existent between the parameters, all together they lead to a puzzle against which I'm struggling since a couple of hours.
Is there any clever solution to do that?
So far, I have been able to solve it with intermediate structures, but I don't like it so much...
Partial specializations don't accept default parameters, but the primary does. You can just add it there:
template<typename Sig, typename X = S<return_type_t<Sig>>>
struct B;
Then all you need to do is implement a return type metafunction for a signature. Something like:
template <class Sig>
struct return_type;
template <class Sig>
using return_type_t = typename return_type<Sig>::type;
template <class R, class... Args>
struct return_type<R(Args...)> {
using type = R;
};
You may create an helper class for that:
template <typename T> struct default_arg;
template <typename R, typename... Args>
struct default_arg<R(Args...)>
{
using type = S<R>;
};
template<typename Sign, typename T = typename default_arg<Sign>::type>
struct B;
Demo
Here we change B into a template alias.
B_t does the default arg work.
B_impl is the implementation of B without any default args.
B is a using alias that gets the result of B_t.
template<class> struct S {};
template<class, class>
struct B_impl;
template<class R, class... Args, template<class...> class C>
struct B_impl<R(Args...), C<R>> {
void f() { }
};
template<class, class=void>
struct B_t;
template<class R, class...Args>
struct B_t<R(Args...), void>:
B_t<R(Args...),S<R>>
{};
template<class R, class... Args, template<class...> class C>
struct B_t<R(Args...), C<R>> {
using type=B_impl<R(Args...), C<R>>;
};
template<class Sig, class Z=void>
using B=typename B_t<Sig,Z>::type;
The downside is that pattern-matching on B won't work well.