Deduce parent class of inherited method in C++ - c++

Take the following class hierarchy:
template<typename T>
class Foo {
public:
T fooMethod() { ... }
};
class Moo : public Foo<bool> {
...
};
If I now somewhere write Moo::fooMethod the compiler will deduce Foo<bool>::fooMethod. How can I deduce Foo<bool> as parent of fooMethod myself before compile time?
Motivation: the compiler will not allow Foo<bool>::fooMethod to be passed as template parameter for bool (Moo::*)() since it will be of type bool (Foo<bool>::*)() in that context. But since I have multiple inheritance I dont know what parent fooMethod will be in, it must be deduced.

If I understand the problem correctly, it is possible to deduce the class a member function is defined in, using the following trait:
template<typename>
struct member_class_t;
template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...)> { using type = C; };
template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...) const> { using type = C const; };
// ...other qualifier specializations
template<typename M>
using member_class = typename member_class_t <M>::type;
after which you can write
member_class<decltype(&Moo::fooMethod)>
giving Foo<bool>.
To define member_class more generally, you should take into account volatile and ref-qualifiers as well, yielding a total of about 12 specializations. A complete definition is here.

Related

Deduce template parameter from concept

I'm learning templates and concepts. I'm trying to make a concept for types that are derived from a class, but this class is a template.
template<typename T>
struct CAA{};
template<typename T, typename T2>
concept DerivedFromAA = requires() {std::derived_from<CAA<T2>,T>;};
Is it possible to use such concept in a function without having to explicitly tell it the template type of the class? Is my idea wrong on how to define such concept?
template<typename T>
void conceptTestFunc(DerivedFromAA<T> auto& aa)
{
}
//...
CAA<int> aa;
conceptTestFunc<int>(aa); // Without having to tell it "int"
(I'm compiling this with Clang.)
A template is not a type.
So if CAA is a template, then CAA<int> would be a type.
A type can't be derived from a template, only from another type. This means that the check has to be done on the type, not the template.
If you on the other hand want to deduce the inner type of aa, that can be done.
#include <concepts>
template<typename T>
struct CAA{};
template<typename T>
struct CBB{};
template<typename T, typename T2>
concept DerivedFromAA = std::derived_from<CAA<T2>,T>;
template<template <typename> typename Outer, typename T>
requires DerivedFromAA<Outer<T>, T>
void conceptTestFunc(Outer<T>& aa)
{
}
int main() {
CAA<int> aa;
conceptTestFunc(aa);
CBB<int> bb;
conceptTestFunc(bb); // This fails
}
You might do
template<typename T>
concept DerivedFromAA = requires(T t) {[]<typename U>(CAA<U>&){}(t);};
static_assert(DerivedFromAA<CAA<int>>);
Demo
gcc dislikes lambda in requires, so you might create dummy helper function outside.

Is it possible to access child types in c++ using CRTP?

I have this toy example,
template <typename TChild>
struct Base {
template <typename T>
using Foo = typename TChild::template B<T>;
};
struct Child : Base<Child> {
template <typename T>
using B = T;
};
using Bar = Child::Foo<int>;
which fails to compile. The intention is that I have a parent class that provides type computations based on members of the child class. The child class is provided via CRTP. However the line
using Foo = typename TChild::template B<T>;
fails to compile:
<source>: In instantiation of 'struct Base<Child>':
<source>:16:16: required from here
<source>:13:11: error: invalid use of incomplete type 'struct Child'
13 | using Foo = typename TChild::template B<T>;
| ^~~
<source>:16:8: note: forward declaration of 'struct Child'
16 | struct Child : Base<Child> {
| ^~~~~
Am I being naive in expecting such a construct to work?
Failing code at https://godbolt.org/z/5Prb84
Let me post another way to do it:
template<typename TChild, class T>
struct GetB {
using Type = typename TChild::template B<T>;
};
template<typename TChild>
struct Base {
template<typename T>
using Foo = typename GetB<TChild, T>::Type;
};
struct Child : Base<Child> {
template<typename T>
using B = T;
};
I don't have a language-lawyer-type explanation why this works, but it should be related to having an additional level of indirection. When a compiler sees
using Foo = typename TChild::template B<T>;
it can (and will) check and complain right at this point that an incomplete type is used. However, when we wrap access to B<T> into a function or a struct,
using Foo = typename GetB<TChild, T>::Type;
then we're not accessing internals of TChild at this point, we're just using the name of it, which is fine.
Issue with CRTP is that derived class is incomplete in CRTP definition, so you cannot use its using.
in
template <typename T>
using Foo = typename TChild::template B<T>;
complete type of TChild would be required because of the ::.
TChild is non-dependent of template T, so first pass check should be done (but fails)
You might use external traits to handle that situation
template <typename C, typename T>
struct Traits_For_Base
{
using type = typename C::template B<T>;
};
template <typename TChild>
struct Base {
template <typename T>
using Foo = typename Traits_For_Base<TChild, T>::type;
};
Traits_For_Base<TChild, T> is dependent of T, so nothing to do for first pass check.
and, with second pass check (dependent of T), Child would be complete.
Demo
or you might change your alias to make in type dependent of the template parameter of the class Base:
template <typename TChild>
struct Base {
template <typename T,
typename C = TChild,
std::enable_if_t<std::is_same_v<C, TChild>, int> = 0> // To avoid hijack
using Foo = typename C::template B<T>;
};
C is dependent of template, so cannot be checked in first phase.
Demo
The following gives the exact same result as required. It's still a mystery as to why it should work when the previous technique does not.
#include <type_traits>
#include <string>
template <typename TChild>
struct Base {
template <typename T>
static auto foo(){
return typename TChild::template B<T>();
}
template <typename T>
using Foo = std::decay_t<decltype(foo<T>())>;
};
struct Child : Base<Child> {
template <typename T>
using B = T;
};
static_assert(std::is_same<Child::B<int>,int>::value,"");
static_assert(std::is_same<Child::B<std::string>,std::string>::value,"");
https://godbolt.org/z/b6Y5Tb
The problem is at stands is that instantiation of nested template requires a complete type of enclosing class and a declaration of template B:
template <typename TChild>
struct Base {
// TChild should be complete at the moment of this declaration
// template B should be declared at this moment.
template <typename T>
using Foo = typename TChild::template B<T>;
};
Instantiation of Base
struct Child : Base<Child>
/* TChild = Child at this moment is incomplete */
{
template <typename T>
using B = T;
/* Point where template B begins to exist */
}; /* point where Child is complete */
Those rules are by design of language, and their goal is to avoid forcing compiler to make multiple passes forth and back through code, possibly in infinite recursion, to actually instantiate what did you mean. Weak-typed interpreter languages often don't have such problem as they can "correct" themselves later.
Case 1.
The static function solution works for the reason that there is no type declaration was done. You had declared a template of function which actually has a global scope, but a concrete function or type aren't created yet.
struct Base {
template <typename T>
static auto constexpr getFoo() {
return typename TChild::template B<T>{};
}
};
struct Child : Base<Child> {
template <typename T>
using B = T;
/* At this point we can instantiate Child::getFoo<int>()*/
}; /* Child is complete now */
At this point instantiation of Child::getFoo<T> is possible but all it requires is the return type of function.
using Bar = decltype(Child::getFoo<int>());
You can place this declaration into Child AFTER the declaration B, because B<int> would be complete at this point. You still can't declare it in Base
Case 2.
Your solution declares another template Foo, it doesn't instantiate it within Base. This template isn't explicitly dependant on TChild but required prototype of foo() to exist at point of instantiation.
template <typename TChild>
struct Base {
template <typename T>
static auto foo(){
return typename TChild::template B<T>();
}
// Foo is a template
template <typename T>
using Foo = std::decay_t<decltype(foo<T>())>;
};
The instantiation happens at point where you would use Base::Foo<T>, which you actually didn't. That declaration is a no-op in your solution. It is legal to use it after the B declaration. You can't use it within Base or anywhere before B was declared.
Now what if you actually need to use an instance of B in Base? Here comes trait class solution:
Case 3.
Traits can be templates specialized for child classes or concrete classes, that's a design choice. A trait serves as a base class for the CRTP base class and is a form of mixin. It's role is to provide useful declarations for CRTP. One of possible solutions for the most flexible trait naming:
template <typename TChild, template<class> typename Trait>
struct Base : public Trait<TChild> {
// Trait<TChild>:: tells compiler that Foo is dependant on TChild
// and is declared in base class Trait. As compiler had reached this
// point, the substitution was successful and thus Trait is complete
using Foo = typename Trait<TChild>::template B<int>;
// Foo is assumed to be a complete type, we can use it here!
Foo make_foo() { return Foo{}; }
};
// Declaring trait template in this case.
template <typename T> struct ChildTrait;
// And specializing
template <>
struct ChildTrait<struct Child> {
template <typename T>
using B = T;
};
struct Child : Base<Child,ChildTrait> {
using Bar = typename Base::Foo;
};
static_assert(std::is_same<Child::B<int>,int>::value,"");
static_assert(std::is_same<Child::B<std::string>,std::string>::value,"");
The idea here is that Trait<TChild> = ChildTrait<Child> has to be and can be a complete class within Base, or we could not derive Base from it. With slight modifications (eliding using, static_assert, typename use) this would compile in C++98 as it doesn't require decltype. This method used by some implementation of standard components, e.g. by std:: streams.
Traits may describe concrete storage types, allocators, etc. WHat's important is that resulting concrete types have no relation but have a shared interface declared in Base.
Does this in any way do what you mean?
#include <type_traits>
template <typename TChild>
struct Base {
template <typename T>
static auto constexpr getFoo() {
return typename TChild::template B<T>{};
}
};
struct Child : Base<Child> {
template <typename T>
using B = T;
};
using Bar = decltype(Child::getFoo<int>());
static_assert(std::is_same_v<Bar, int>);
I've essentially replaced the template alias with a template static function which returns a default constructed object of the type you wanted the template alias to encode, so decltype-ing its result should give the type you want.

MSVC not recognizing 'direct' base class constructor of template class inheriting a template class

This is not a duplicate of this question.
Below, I list my base class as well as how I am deriving a class from it:
template<template<size_t, typename...> typename D>
struct Modifier
{
// some static variables
};
template<typename M, size_t U, typename T, typename... S>
struct Base
{
// some implementations
};
template<size_t U, typename T>
struct Derived;
template<>
struct Modifier<Derived>;
template<size_t U, typename T>
struct Derived : Base<Modifier<Derived>, U, T, double>
{
using Base<Modifier<Derived>, U, T, double>::Base;
};
To provide some context, the idea is that the implementation of Base will access some static variables from M in order to change the behaviour of a couple of functions.
When I compile this with msvc (build tools v142), it complains about:
1>...: error C3210: 'Base<Modifier<Derived<2,unsigned __int64> >,2,unsigned __int64,double>': a member using-declaration can only be applied to a base class member
1>...: error C3881: can only inherit constructor from direct base
Yet, when I introduce using mod_t = Modifier<Derived>; and apply that in the implementation of Derived instead of the full name, it correctly compiles.
By the way, whether I define Derived like above or like:
template<size_t U, typename T>
struct Derived : Base<Modifier<Derived>, U, T, double>
{
using parent_type = Base<Modifier<Derived>, U, T, double>;
using parent_type::Base;
};
or
template<size_t U, typename T>
struct Derived : Base<Modifier<Derived>, U, T, double>
{
using parent_type = Base<Modifier<Derived>, U, T, double>;
using parent_type::parent_type;
};
I will still get an error.
What am I missing here?
This issue (with msvc) is actually related to my previous question here:
Simpler work-around to avoid injected-class-name is to use full name.
In the above, Derived needs to be prefixed as ::Derived and this will then successfully compile.

How std::conditional works

We have this little metaprogramming marvel called std::conditional described here. In the same reference it says that a possible implementation is
template<bool B, class T, class F>
struct conditional { typedef T type; };
template<class T, class F>
struct conditional<false, T, F> { typedef F type; };
So if in code I do something like
typename std::conditional<true,int,double>::type a;
the compiler will follow the first definition and if I do something like
typename std::conditional<false,int,double>::type b
the compiler will take the second. Why does that work ? What compilation rule is in place here ?
In short it has to do with template specialization rules. These are like function overloads but for types.
Here compiler prefers a more specialized type to the more general type.
template<bool B, class T, class F>
struct conditional { typedef T type; };
template<class T, class F>
struct conditional<false, T, F> { typedef F type; };
So if a template instantiation coditional<false, ...> is seen by compiler it finds:
the general template template<bool B, class T, class F> struct conditional
all its specializations: template<class T, class F> struct conditional<false, T, F>
At that point it tries to match as many specialized arguments as possible and ends up selecting the specialization with false.
For example introducing another version like:
template<class F>
struct conditional<false, int, F> { typedef int type; };
and instantiating a template type like conditional<false, int, double> will prefer specialization
template<class F> struct conditional<false, int, F>
to
template<class T, class F> struct conditional<false, T, F> which is more general compared to the version with 2 specialized paramaters.
Some tricks at this point:
It is even OK to just declare the most generic case (i.e. generic form of the template is just declared but not defined) and only have specializations for cases you really intend to have. All non-specialized cases will result in compile errors and asking the user to specialize the case for a particular type.
Why does that work ? What compilation rule is in place here ?
I'm not an expert but I'll try to explain from the pratical point of view.
Hoping to use the rights terms...
With
template <bool B, class T, class F>
struct conditional { typedef T type; };
(but, with C++11, I prefer
template <bool B, typename T, typename>
struct conditional { using type = T; };
) you declare the template
template <bool, typename, typename>
struct conditional;
and define the generic (not specialized) version.
With
template< class T, class F>
struct conditional<false, T, F> { typedef F type; };
(or, in C++11,
template <typename T, typename F>
struct conditional<false, T, F> { using type = F; };
) you define a partial specialization of conditional
When the template arguments of a class (or struct) match two or more definitions, the compliler choose the more specialized one; so, for
typename std::conditional<false,int,double>::type
both definitions of the class match so the compiler choose the specialized one (the specialized with false) anche type is double.
For
typename std::conditional<true,int,double>::type a;
only the generic version match, so type is int.
In most of the template meta programming main rule at play here is SFINAE(Substitution failure is not an error). According to this rule if compiler not find the type then instead of throwing an error it moves forward and check if there is any future statement that can provide type information.

Simple concept check

Say I have a simple template like this:
template<typename T>
class A {};
And I want to specify that the type-parameter T is of some unrelated type X<U> where U is not known (or unspecifyable).
Is there a way how to express that as a concept?
Is there a way how to express that as a concept?
You don't need a concept, class template specialization works just fine in your case.
As an example, you can do this:
template<typename T>
class A;
template<typename U>
class A<X<U>> { /* ... */ };
This way, unless A is instantiated with a type of the form X<U> (where U is unknown), you'll get a compile-time error because the primary template isn't defined. In other terms, it won't work for all the types but X<U> (for each U), where the latter matches the class template specialization that has a proper definition.
Note that I assumed X is a known type. That's not clear from your question.
Anyway, if it's not and you want to accept types of the form X<U> for each X and each U, you can still do this:
template<typename T>
class A;
template<template<typename> class X, typename U>
class A<X<U>> { /* ... */ };
As a minimal, working example:
template<typename>
struct S {};
template<typename>
class A;
template<typename U>
class A<S<U>> {};
int main() {
A<S<int>> aSInt;
A<S<double>> aSDouble;
// A<char> aChar;
}
Both A<S<int>> and A<S<double>> are fine and the example compiles. If you toggle the comment, it won't compile anymore for A<char> isn't defined at all.
As a side note, if you don't want to use class template specialization and you want to simulate concepts (remember that they are not part of the standard yet and they won't be at least until 2020), you can do something like this:
#include<type_traits>
template<typename>
struct X {};
template<typename>
struct is_xu: std::false_type {};
template<typename U>
struct is_xu<X<U>>: std::true_type {};
template<typename T>
struct A {
static_assert(is_xu<T>::value, "!");
// ...
};
int main() {
A<X<int>> aXInt;
A<X<double>> aXDouble;
// A<char> aChar;
}
That is, given a generic type T, static assert its actual type by means of another structure (is_xu in the example) that verifies if T is of the form X<U> (for each U) or not.
My two cents: the class template specialization is easier to read and understand at a glance.
template <typename T, template <typename> class C>
concept bool Template = requires (T t) { {t} -> C<auto>; };
Now given a class template:
template <typename T>
struct X {};
a type template parameter can be constrained using:
template <typename T> requires Template<T, X>
class A {};
or:
template <Template<X> T>
class A {};
DEMO
This will work also for types derived from X<U>.