Can anyone tell my how to enable if/else class member template based on different derived classes from pre-defined base set? Let me use the following example:
enum class Type {
TYPEA,
TYPEB
};
// Predefined in libraries.
class BaseA {...};
class BaseB {...};
class Foo {
template <typename Derived, Type type>
void foo();
};
// User-derived
class DerivedA : public BaseA {};
class DerivedB : public BaseB {};
Normally we need two template typenames for calling the member foo.
Foo obj;
obj.foo<DerivedA, Type::TypeA>()
obj.foo<DerivedB, Type::TypeB>();
However, this native approach seems lengthy because the second template argument Type::TypeA and Type::TypeB can obviously be deduced by compiler through the first argument DerivedA and DerivedB, if they are derived from pre-defined base properly. I notice that c++11 provides is_base_of template but I am not sure how to use it in my case. To be more specific, below is the expected solution:
obj.foo<DerivedA>(); // Automatically deduce type = Type::TypeA
obj.foo<DerivedB>(); // Automatically deduce type = Type::TypeB
And if the compile fails to deduce the Type from the first typename, it should it just goes back to the normal declaration obj.foo<MyClass, MyType> where MyType is either Type::TypeA or Type::TypeB.
Sounds like you just want a default template argument:
class Foo {
template <typename Derived, Type type = get_type_from<Derived>::value>
void foo();
};
Where get_type_from<> is a metafunction to be filled in later based on how you actually figure out the Types.
template<Type t>
using etype_tag = std::integral_constant<Type, t>;
template<class T>
struct tag_t {
using type=T;
template<class D,
std::enable_if_t<std::is_base_of<T, D>::value, int>* =nullptr
>
constexpr tag_t( tag_t<D> ) {}
constexpr tag_t() = default;
constexpr tag_t(tag_t const&) = default;
};
template<class T>
constexpr tag_t<T> tag{};
constexpr etype_tag<Type::TYPEA> get_etype( tag_t<BaseA> ) { return {}; }
constexpr etype_tag<Type::TYPEB> get_etype( tag_t<BaseB> ) { return {}; }
template<class T>
constexpr decltype( get_etype( tag<T> ) ) etype{};
Now etype<Bob> is a compile-time constant integral constant you want.
class Foo {
template <typename Derived, Type type=etype<Derived>>
void foo();
};
makes the 2nd argument (usually) redundant.
You can extend get_etype with more overloads in either the namespace where etype is declared, or in the namespace of tag_t, or in the namespace of the type you are extending get_etype to work with, and etype will automatically gain support (assuming it is used in a context where the extension is visible: failure of that requirement leaves your program ill formed).
Live example
Related
I have a class that works as wrapper for some primitives or custom types. I want to write explicit specialization for custom template type.
My code that reproduces the problem:
template < class T >
struct A {
void func() { std::cout << "base\n"; }
};
template <>
struct A<int> {};
template < class T, class CRTP >
struct BaseCrtp {
void someFunc() {
CRTP::someStaticFunc();
}
};
struct DerrType : BaseCrtp<int, DerrType> {
static void someStaticFunc() {}
};
template < class T, class CRTP >
struct A< BaseCrtp<T, CRTP> > {
void func() { std::cout << "sometype\n"; }
};
int main() {
A<DerrType> a;
a.func(); // print: "base". should be: "sometype"
return 0;
}
A<DerrType> use default function, not a specialization. How can I make specialization for these set of classes?
I will have a lot of types like DerrType, and I want to make common behavior for all of them.
DerrType and others will be used as curiously recurring template pattern
Not sure I fully understood what you want, but maybe something like this:
template<typename T>
concept DerivedFromBaseCrtp = requires(T& t) {
[]<typename U, typename CRTP>(BaseCrtp<U, CRTP>&){}(t);
};
template < DerivedFromBaseCrtp T >
struct A<T> {
void func() { std::cout << "sometype\n"; }
};
The concept basically checks whether T is equal to or is publicly inherited (directly or indirectly) from some specialization of BaseCrtp. Otherwise the call to the lambda would be ill-formed. Template argument deduction only succeeds in the call if the argument and parameter type match exactly or the argument has a derived type of the parameter. If the class is inherited non-publicly, the reference in the call can't bind to the parameter.
The concept will however fail if the type is inherited from multiple BaseCrtp specializations, in which case template argument deduction on the call will not be able to choose between the multiple choices.
Alternatively you can also use the stricter concept
template<typename T>
concept CrtpDerivedFromBaseCrtp = requires(T& t) {
[]<typename U>(BaseCrtp<U, T>&){}(t);
};
which will also require that the type T is actually using the CRTP pattern on BaseCrtp (directly or through a some base class between BaseCrtp and T). Again, this will fail if T is inherited multiple times from some BaseCrtp<U, T> specialization, although it will ignore specializations with a type other than T in the second position.
For another alternative you might want to check that T is derived from some type X such that X is derived from BaseCrtp<U, X> for some U (meaning that X uses the CRTP pattern correctly). That could be done using this variation:
template <typename T>
concept CrtpDerivedFromBaseCrtp =
requires(T& t) {
[]<typename U, typename CRTP>(BaseCrtp<U, CRTP>&)
requires(std::is_base_of_v<CRTP, T> &&
std::is_base_of_v<BaseCrtp<U, CRTP>, CRTP>)
{}
(t);
};
Again, this fails if T is derived from multiple BaseCrtp specializations, directly or indirectly.
This is NOT a duplicate of link
Consider the following code:
#include <type_traits>
template <typename... Bases>
struct Overloads : public Bases... {};
template <typename T>
struct A {
using AType = T;
};
template <typename T>
struct B {
using BType = T;
};
template <typename T>
struct C {
using CType = T;
};
template <typename OverloadsType>
struct Derived : public OverloadsType {
};
int main() {
// OK
static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::AType, int>);
// OK
static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::BType, float>);
// OK
static_assert(std::is_same_v<typename Derived<Overloads<A<int>, B<float>, C<char>>>::CType, char>);
// ???
static_assert(std::is_same_v<typename Derived<Overloads<B<float>, C<char>>>::AType, void>);
}
Demo: Link
For the last line, I need to detect that Derived<Overloads<B<float>, C<char>>> is NOT derived from A, so I want typename Derived<Overloads<B<float>, C<char>>>::AType to be void or something (it fails to compile)
How can I do this?
My precise use case is: 1. Determine if Derived is derived from A<T> for some T. 2. If so, figure out that T.
With C++20 concepts, this isn't too difficult. You need a function that takes A<T> as a parameter. There need not be a function definition; we're just using template argument deduction. It will never be called:
template<typename T>
T getDerivedFromAType(A<T> const&); //Not implemented, since we will never call it.
Any type U for which getDerivedFromAType(u) works will either be an A<T> itself or a type derived from A<T> (from a single A<T> of course). So we can build a concept out of it:
template<typename U>
concept IsDerivedFromA = requires(U u)
{
getDerivedFromAType(u);
};
So long as nobody writes an overload of getDerivedFromAType, you're fine. This function ought to be in a detail namespace or have something else that lets people know that it is off-limits.
To get the actual T used by A... well, there's a reason why the function returned T. We simply need a using statement that calculates the return type of the function call:
template<IsDerivedFromA T>
using BaseAType = decltype(getDerivedFromAType(std::declval<T>()));
Notice that the template is guarded by our concept.
Library code
My library has a CRTP class B<Derived>.
I created a Trait<T> class to enable user to change behavior of B.
The default setting is int. (#1)
#include <iostream>
#include <string>
//B and Trait (library class)
template<class Derived> class B;
template<class T>class Trait{
public: using type = int; //<-- default setting //#1
};
template<class Derived> class B{
public: using type = typename Trait<Derived>::type; //#2
public: type f(){return 1;}
};
User code ( full coliru demo )
Then, I create a new class C with a new setting std::string. (#3)
It works fine.
//C (user1's class)
template<class Derived> class C ;
template<class Derived>class Trait<C<Derived>>{
public: using type = std::string; //#3
};
template<class Derived> class C : public B<Derived>{};
Finally, I create a new class D.
I want D to derive C's setting i.e. std::string (not int).
However, it is not compilable at $.
//D (user2's class)
class D : public C<D>{ //#4
public: type f(){return "OK";} //#5
//$ invalid conversion from 'const char*' to 'B<D>::type {aka int}'
};
int main(){
D dt;
std::cout<< dt.f() <<std::endl;
}
My understanding
Roughly speaking, here is my understanding about the compile process :-
Just before class D (#4), it doesn't know about D.
At #4, to identity D::type, it looks up C<D>::type.
Finally, it finds that it is defined in B<D>::type at #2.
From #2, it travels to the definition at #1 and find type = int.
Thus D::type = int.
Note that #3 is ignored, because at this point (#4 and #5), D is still incomplete.
The compiler still doesn't fully recognize that D is derived from C<something> ... yet.
Question
How to let D automatically inherit Trait's setting from C without explicitly define another template specialization Trait<D>?
In other words, how to make #3 not ignored for D?
Trait is probably not a good design (?), but I prefer to let the type setting be in a separate trait class.
The instantiating goes like this:
D -> C<D> -> B<D> -> Traits<D>
Traits<D> does not match you partial specialization of Traits<C<Derived>>
If you change it to template<class Derived> class C : public B<C<Derived>>{}; that will in turn instantiate Traits<C<D>> and that will match your specialization and you get std::string as type.
To get the child from B you can use.
template <typename... T>
struct getChild;
template <template <typename... T> typename First, typename... Rest>
struct getChild<First<Rest...>> { using child = typename getChild<Rest...>::child; };
template <typename First>
struct getChild<First> { using child = First; };
and then add in
template<class Derived> class B{
public: using type = typename Trait<Derived>::type;
using child = typename getChild<Derived>::child;
public: type f(){return 1;}
};
I've been experimenting with the Curiously Recurring Template Pattern for a generic single-argument functor and have two implementations: one using a template template parameter which works and a second where I try to access the derived Functor::type in the interface class. In the latter example, the compiler (gcc 5.4.0) reports
error: no type named 'type' in 'struct Cube< double >'
template<class T, template<class> class Functor>
class FunctorInterface_1 {
private:
const Functor<T> &f_cref;
public:
FunctorInterface_1() : f_cref(static_cast<const Functor<T>&>(*this)) {}
T operator() ( T val ) const { return f_cref(val); }
}; // FunctorInterface_1 (works)
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
using Ftype = typename Functor::type;
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
Ftype operator() ( Ftype val ) const { return f_cref(val); }
}; // FunctorInterface_2 (no type in Functor!)
I then try to compile with T=double in main() of the following two classes:
template<class T>
struct Square : public FunctorInterface_1<T,Square> {
T operator()( T val ) const { return val*val; }
}; // Square
template<class T>
struct Cube : public FunctorInterface_2<Cube<T>> {
using type = T;
T operator() ( T val ) const { return val*val*val; }
}; // Cube
Can the FunctorInterface_2/Cube example be modified to work, or
is it necessary for the interface class to be templated on T as
in the first example? Thanks!
EDIT: Using gcc -std=c++14, I can get the second example to compile and run
by using auto return and argument types in FunctorInterface_1::operator(), however, as I understand, auto argument types are not part of the C++14 standard.
EDIT 2: Well I feel a bit thick. I just realized that I could template FunctorInterface_1::operator() on a new parameter, however, for the application I have in mind, I would really like my base class to be able to access types defined in the derived class.
When the line
using Ftype = typename Functor::type;
is processed in the base class, the definition of Functor is not available. Hence, you can't use Functor::type.
One way to get around this limitation is to define a traits class.
// Declare a traits class.
template <typename T> struct FunctorTraits;
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
// Use the traits class to define Ftype
using Ftype = typename FunctorTraits<Functor>::type;
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
Ftype operator() ( Ftype val ) const { return f_cref(val); }
}; // FunctorInterface_2 (no type in Functor!)
// Forward declare Cube to specialize FunctorTraits
template<class T> struct Cube;
// Specialize FunctorTraits for Cube
template <typename T> struct FunctorTraits<Cube<T>>
{
using type = T;
};
template<class T>
struct Cube : public FunctorInterface_2<Cube<T>> {
using type = T;
T operator() ( T val ) const { return val*val*val; }
}; // Cube
Working code: https://ideone.com/C1L4YW
Your code could be simplified to
template<typename TDerived> class
Base
{
using Ftype = typename TDerived::type;
};
template<typename T> class
Derived: public Base<Derived<T>>
{
using type = T;
};
Derived<int> wat;
It does not work because at the point of Base instantiation Derived class is not complete, and compiler is not aware of Derived::type existence yet.
You have to understand that when you instantiate Cube<T> FunctionInterface_2<Cube<T>> gets instantiated first. This means that Cube<T> is an incomplete type while this is happening.
So when the compiler gets to the line using Ftype = typename Functor::type; Functor is incomplete and you cannot access any of its nested types.
In your case you can change FunctionInterface_2 to:
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
template <class TT>
auto operator() ( TT && val ) -> decltype(f_cref(val)) const { return f_cref(val); }
};
So now accessing information about Functor is delayed until you call the operator() from FunctionInterface_2 at which point FunctionInterface_2 and Cube are fully instantiated.
Note: This question has already been answered by #r-sahu, but I'd like to elaborate on this and address clang's output specifically.
The problem can be demonstrated on a much smaller code sample: (#vtt suggested something similar)
template <typename _CRTP>
struct A {
using _C = typename _CRTP::C;
};
struct B : public A<B> {
using C = int;
};
Compiling that with clang will result in completely misleading error message: (godbolt)
<source>:3:32: error: no type named 'C' in 'B'
using _C = typename _CRTP::C;
~~~~~~~~~~~~~~~~^
<source>:6:19: note: in instantiation of template class 'A<B>' requested here
struct B : public A<B> {
^
1 error generated.
GCC's error message is a little more helpful: (godbolt)
<source>: In instantiation of 'struct A<B>':
<source>:6:19: required from here
<source>:3:33: error: invalid use of incomplete type 'struct B'
3 | using _C = typename _CRTP::C;
| ^
<source>:6:8: note: forward declaration of 'struct B'
6 | struct B : public A<B> {
| ^
As suggested in the accepted answer, implementing a trait type fixes the issue:
// this declaration must appear before the definition of A
template <typename _A>
struct a_traits;
template <typename _CRTP>
struct A {
// `a_traits<_CRTP>::type` is an incomplete type at this point,
// but that doesn't matter since `A` is also incomplete
using _C = typename a_traits<_CRTP>::type;
};
// this specialization must appear before the definition of B
template <>
struct a_traits<struct B> { // adding the type specifier `struct` will declare B
using type = int;
};
// specifying the template parameter will complete the type `A<B>`, which works since
// `a_traits<B>` is already complete at this point
struct B : public A<B> {
using C = int;
};
Inspired of boost::operators I thought the Barton-Nackman idiom could be used to implement from trival member methods.
Following is what I have tried (does not compile)
template<typename T>
class impl_get_set {
typename T::storage_type get() const {
return static_cast<const T *>(this)->data_;
}
void set(typename T::storage_type d) {
*static_cast<T *>(this)->data_ = d;
}
};
struct A : public impl_get_set<A> {
typedef int storage_type;
storage_type data_;
};
struct B : public impl_get_set<B> {
typedef double storage_type;
storage_type data_;
};
As this does not compile there is clearly something I have got wrong. My question is, can this be done, and if so how?
When using CRTP, you have to take care when designing the base, i.e. impl_get_set in this case. When the derived class instantiates the base specialization, e.g. as done with A: public impl_get_set<A>, the A class is still incomplete.
However the definition of impl_get_set uses typename T::storage_type in a member function declaration. This use requires a complete T. The C++03 way to solve that is to make any associated type that the CRTP base may need part of the class template parameters:
template<typename Derived, typename StorageType>
struct get_set {
typedef StorageType storage_type;
// It's possible to define those inline as before where
// Derived will be complete in the body -- which is why
// CRTP is possible at all in the first place
storage_type get() const;
void set(storage_type s);
// Convenience for clients:
protected:
typedef get_set get_set_base;
};
struct A: get_set<A, int> {
// Member type is inherited
storage_type data;
};
template<typename T>
struct B: get_set<B<T>, double> {
// Incorrect, storage_type is dependent
// storage_type data;
// First possibility, storage_type is
// still inherited although dependent
// typename B::storage_type data;
// Second possibility, convenient if
// storage_type is used multiple times
using typename B::get_set_base::storage_type;
storage_type data;
void foo(storage_type s);
};
boost::iterator_facade is a good example of a well-written C++03-style CRTP wrapper from Boost.Iterator.
C++11 gives another way to write a CRTP base thanks in part to default template arguments for function templates. By making the derived class parameter dependent again, we can use it as if it were complete -- it will only be examined when the member function template of the CRTP base specialization is instantiated, once it is complete, and not when the CRTP base specialization itself is:
// Identity metafunction that accepts any dummy additional
// parameters
template<typename T, typename... Dependent>
struct depend_on { using type = T; };
// DependOn<T, D> is the same as using T directly, except that
// it possibly is dependent on D
template<typename t, typename... D>
using DependOn = typename depend_on<T, D...>::type;
template<typename Derived>
struct get_set {
template<
// Dummy parameter to force dependent type
typename D = void
, typename Storage = typename DependOn<Derived, D>::storage_type
>
Storage get() const
{
// Nothing to change, Derived still complete here
}
};
In fact, for your example get_set arguably doesn't need to care about whether a member type is present or not:
// std::declval is from <utility>
template<
typename D = void
, typename Self = DependOn<Derived, D>
>
auto get() const
-> decltype( std::declval<Self const&>().data )
{ return static_cast<Derived const&>(*this).data; }
This implementation of get has subtly different semantics from your own in that it returns a reference to data but that's on purpose.
The best I could figure out is that you're in a chicken/egg problem.
struct A uses impl_get_set as base, that forces instantiation. But at that point A is incomplete, its contents not yet available. Therefore T::storage_type fails to resolve to anything.
The only workaround I found was to just have another template param for impl_get_set and pass it from above. So go the opposite direction:
template<typename T, typename ST>
class impl_get_set {
public:
typedef ST storage_type;
storage_type get() const {
return static_cast<const T *>(this)->data_;
}
void set(storage_type d) {
*static_cast<T *>(this)->data_ = d;
}
};
struct A : public impl_get_set<A, int> {
storage_type data_;
};
(A is currently not used in the base, I left it in for possible other plans)