C++ - using base class as template parameter [duplicate] - c++

This question already has answers here:
Restricting templates to only certain classes?
(7 answers)
Closed 2 years ago.
I'd like my template function to accept as an argument only a class that inherits from a base class. I think a code snippet explains it better.
class Base
{
// Some magic
}
class Derived : public Base
{
// Even more magic
}
class Foo
{}
// Is it possible to tell template to accept only classes derived from Base?
template<class T>
do_something(T obj)
{
// Perform some dark magic incantations on obj
}
int main()
{
Foo foo;
Derived derived;
do_something<Derived>(derived); // Normal
do_something<Foo>(foo); // Compilation error if I understand templates correctly
}

Pre-C++20 you can use enable_if coupled with checking is_base_of:
template<class T, std::enable_if_t<std::is_base_of_v<Base, T> && !std::is_same_v<Base, T>, int> = 0>
void do_something(T obj)
{
// Perform some dark magic incantations on obj
}
Note that I've explicitly disallowed the type to be an instance of Base (because is_base_of considers a type to be a base of itself). If you want to allow instances of Base, then remove && !std::is_same_v<Base, T>
Live Demo
In C++20 we can almost directly translate our enable_if into a requires expression:
template<class T>
requires (std::is_base_of_v<Base, T> && !std::is_same_v<Base, T>)
void do_something(T obj)
{
// ...
}
Concepts Demo
Or, if you want to allow instances of Base, you can use the built-in derived_from Concept:
template<class T>
requires std::derived_from<T, Base>
void do_something(T obj)
{
// ...
}
Concepts Demo 2

Related

Template specialization for the base template type for future derived types

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.

C++ using declaration for parameter pack

I would like to define a class which inherits from a bunch of classes but which does not hide some specific methods from those classes.
Imagine the following code:
template<typename... Bases>
class SomeClass : public Bases...
{
public:
using Bases::DoSomething...;
void DoSomething(){
//this is just another overload
}
};
The problem is now if just one class does not have a member with the name DoSomething I get an error.
What I already tried was emulating an "ignore-if-not-defined-using" with a macro and SFINAE but to handle all cases this becomes very big and ugly!
Do you have any idea to solve this?
It would be really nice if I could define: "Hey using - ignore missing members".
Here I have some sample code: Godbolt
The problem with Jarod42's approach is that you change what overload resolution looks like - once you make everything a template, then everything is an exact match and you can no longer differentiate between multiple viable candidates:
struct A { void DoSomething(int); };
struct B { void DoSomething(double); };
SomeClass<A, B>().DoSomething(42); // error ambiguous
The only way to preserve overload resolution is to use inheritance.
The key there is to finish what ecatmur started. But what does HasDoSomething look like? The approach in the link only works if there is a single, non-overloaded, non-template. But we can do better. We can use the same mechanism to detect if DoSomething exists that is the one that requires the using to begin with: names from different scopes don't overload.
So, we introduce a new base class which has a DoSomething that will never be for real chosen - and we do that by making our own explicit tag type that we're the only ones that will ever construct. For lack of a better name, I'll name it after my dog, who is a Westie:
struct westie_tag { explicit westie_tag() = default; };
inline constexpr westie_tag westie{};
template <typename T> struct Fallback { void DoSomething(westie_tag, ...); };
And make it variadic for good measure, just to make it least. But doesn't really matter. Now, if we introduce a new type, like:
template <typename T> struct Hybrid : Fallback<T>, T { };
Then we can invoke DoSomething() on the hybrid precisely when T does not have a DoSomething overload - of any kind. That's:
template <typename T, typename=void>
struct HasDoSomething : std::true_type { };
template <typename T>
struct HasDoSomething<T, std::void_t<decltype(std::declval<Hybrid<T>>().DoSomething(westie))>>
: std::false_type
{ };
Note that usually in these traits, the primary is false and the specialization is true - that's reversed here. The key difference between this answer and ecatmur's is that the fallback's overload must still be invocable somehow - and use that ability to check it - it's just that it's not going to be actually invocable for any type the user will actually use.
Checking this way allows us to correctly detect that:
struct C {
void DoSomething(int);
void DoSomething(int, int);
};
does indeed satisfy HasDoSomething.
And then we use the same method that ecatmur showed:
template <typename T>
using pick_base = std::conditional_t<
HasDoSomething<T>::value,
T,
Fallback<T>>;
template<typename... Bases>
class SomeClass : public Fallback<Bases>..., public Bases...
{
public:
using pick_base<Bases>::DoSomething...;
void DoSomething();
};
And this works regardless of what all the Bases's DoSomething overloads look like, and correctly performs overload resolution in the first case I mentioned.
Demo
How about conditionally using a fallback?
Create non-callable implementations of each method:
template<class>
struct Fallback {
template<class..., class> void DoSomething();
};
Inherit from Fallback once for each base class:
class SomeClass : private Fallback<Bases>..., public Bases...
Then pull in each method conditionally either from the base class or its respective fallback:
using std::conditional_t<HasDoSomething<Bases>::value, Bases, Fallback<Bases>>::DoSomething...;
Example.
You might add wrapper which handles basic cases by forwarding instead of using:
template <typename T>
struct Wrapper : T
{
template <typename ... Ts, typename Base = T>
auto DoSomething(Ts&&... args) const
-> decltype(Base::DoSomething(std::forward<Ts>(args)...))
{
return Base::DoSomething(std::forward<Ts>(args)...);
}
template <typename ... Ts, typename Base = T>
auto DoSomething(Ts&&... args)
-> decltype(Base::DoSomething(std::forward<Ts>(args)...))
{
return Base::DoSomething(std::forward<Ts>(args)...);
}
// You might fix missing noexcept specification
// You might add missing combination volatile/reference/C-elipsis version.
// And also special template versions with non deducible template parameter...
};
template <typename... Bases>
class SomeClass : public Wrapper<Bases>...
{
public:
using Wrapper<Bases>::DoSomething...; // All wrappers have those methods,
// even if SFINAEd
void DoSomething(){ /*..*/ }
};
Demo
As Barry noted, there are other drawbacks as overload resolution has changed, making some call ambiguous...
Note: I proposed that solution as I didn't know how to create a correct traits to detect DoSomething presence in all cases (overloads are mainly the problem).
Barry solved that, so you have better alternative.
You can implement this without extra base classes so long as you’re willing to use an alias template to name your class. The trick is to separate the template arguments into two packs based on a predicate:
#include<type_traits>
template<class,class> struct cons; // not defined
template<class ...TT> struct pack; // not defined
namespace detail {
template<template<class> class,class,class,class>
struct sift;
template<template<class> class P,class ...TT,class ...FF>
struct sift<P,pack<>,pack<TT...>,pack<FF...>>
{using type=cons<pack<TT...>,pack<FF...>>;};
template<template<class> class P,class I,class ...II,
class ...TT,class ...FF>
struct sift<P,pack<I,II...>,pack<TT...>,pack<FF...>> :
sift<P,pack<II...>,
std::conditional_t<P<I>::value,pack<TT...,I>,pack<TT...>>,
std::conditional_t<P<I>::value,pack<FF...>,pack<FF...,I>>> {};
template<class,class=void> struct has_something : std::false_type {};
template<class T>
struct has_something<T,decltype(void(&T::DoSomething))> :
std::true_type {};
}
template<template<class> class P,class ...TT>
using sift_t=typename detail::sift<P,pack<TT...>,pack<>,pack<>>::type;
Then decompose the result and inherit from the individual classes:
template<class> struct C;
template<class ...MM,class ...OO> // have Method, Others
struct C<cons<pack<MM...>,pack<OO...>>> : MM...,OO... {
using MM::DoSomething...;
void DoSomething();
};
template<class T> using has_something=detail::has_something<T>;
template<class ...TT> using C_for=C<sift_t<has_something,TT...>>;
Note that the has_something here supports only non-overloaded methods (per base class) for simplicity; see Barry’s answer for the generalization of that.

How to customize template to not include certain types [duplicate]

This question already has answers here:
C++ templates that accept only certain types
(14 answers)
restrict a template function, to only allow certain types
(3 answers)
Template excluding one type
(3 answers)
Template Class C++ - exclude some types
(3 answers)
Closed 3 years ago.
Following Template class type-specific functions, how can I customize my template code to not compile for certain types?
If the question is not clear, take a look at this example.
///** template class ***/
template<typename T>
class testClass{
testClass();
T parameter;
}
template<typename T>
void testClass<T>::print(){cout<<parameter.value<<endl;}
The above class is supposed to work for the following types:
//** types file **/
class paramtype1{
int value;
}
class paramtype2{
int value;
}
class paramtype3{
}
As you see, paramtype3 doesn't have value, so I get a compile error saying that value is not defined. I know that if I want to specialize a template class function for a certain type (s), I need to do:
template<>
void testClass<paramtype1>::print(){cout<<parameter.value<<endl;}
But, is there any way to do the other way around, only excluding some certain types?
If you want enable/disable the full class/struct, you can use SFINAE and partial specialization.
The following is a C++17 example
template <typename T, typename = void>
struct testClass;
template <typename T>
struct testClass<T, std::void_t<decltype(T::value)>>
{
testClass()
{ };
T parameter;
void print()
{ std::cout << parameter.value << std::endl; }
};
If you only want enable/disable the print() function, you have to templatize it; by example
template <typename U = T>
std::void_t<decltype(U::value)> print()
{ std::cout << parameter.value << std::endl; }
or also
template <typename U = T>
std::void_t<decltype(U::value), std::enable_if_t<std::is_same_v<U, T>>>
print()
{ std::cout << parameter.value << std::endl; }
if you want to be sure that nobody can "hijack" the method explicating the template type calling
testClass<paramtype3>{}.print<paramtype1>():
What I would personally do to exclude permissions to use certain types is:
template <class T, class... Ts>
struct is_any : std::disjunction<std::is_same<T, Ts>...> {};
// https://stackoverflow.com/questions/17032310/how-to-make-a-variadic-is-same
template <typename T>
void do_something() {
static_assert(!is_any<T, int, bool>::value, "do_something<T> cannot be used with T as int or bool");
// code here
}
Allows you to add a custom assertion message aswel, making it easy to realise what's wrong.

Trouble using SFINAE to switch constructors [duplicate]

This question already has answers here:
Select class constructor using enable_if
(3 answers)
Closed 3 years ago.
I have two constructors that I would like to choose between based on a template parameter yes
template <bool yes>
class Base {
public:
template<typename std::enable_if< yes, int>::type = 0>
Base() { /* yes */ }
template<typename std::enable_if<!yes, int>::type = 0>
Base() { /* no */ }
};
I'm baffled why this produces the compiler error,
failed requirement '!true'; 'enable_if' cannot be used to disable this declaration
on Base<true> and
no type named 'type' in 'std::__1::enable_if<false, int>'; 'enable_if' cannot be used to disable this declaration
on Base<false>. None of the other variants that I can find including this and this and this work either. How can I select which constructor to use based on yes?
There are several issues here. The first thing is that the syntax for default template template paramers is wrong, it should be:
template <bool yes>
class Base {
public:
template<typename T=std::enable_if< yes, int>::type>
Base() { /* yes */ }
template<typename T=std::enable_if<!yes, int>::type>
Base() { /* no */ }
};
But that's not going to work either, because default parameter values are not a part of the template's signature, so, roughly speaking, this is equivalent to:
template<typename T>
Base() { /* yes */ }
template<typename T>
Base() { /* no */ }
That's how the both constructors' signatures look to the compiler. Both are templates with a single parameter, so for the purposes of overload resolutions both constructors have identical signatures, and that's not going to work any better than declaring two "Base(int foo)" constructors. You'll get the same error if you declare:
Base(int foo=0)
and
Base(int foo=1)
constructors. Two constructors, both have the same signature. Default values are not a part of a signature.
There are several traditional hacks to work around this. A common design pattern in the C++ library itself is to declare some helper empty classes and use them as additional parameters to disambiguate different methods for the purposes of overload resolution. For example, using std::in_place_t to select a particular overloaded constructor of std::optional, or std::in_place_type_t, for the equivalent functionality of std::variant's constructor.
In your case here, we can make use of the placeholder parameter completely automatic, in conjunction with delegated constructors:
#include <iostream>
struct bool_true {};
struct bool_false {};
template<bool value> class bool_value;
template<>
struct bool_value<true> {
typedef bool_true type;
};
template<>
struct bool_value<false> {
typedef bool_false type;
};
template<bool v>
using bool_value_t=typename bool_value<v>::type;
template <bool yes>
class Base {
public:
Base() : Base{ bool_value_t<yes>{} } {}
Base(const bool_true &)
{
std::cout << "Yes" << std::endl;
}
Base(const bool_false &)
{
std::cout << "No" << std::endl;
}
};
int main()
{
Base<true> t;
Base<false> f;
return 0;
}

template class restriction

I'm wondering if there is any way to restrict generating code for a template using custom conditions in my case i want to function foo to be called only if template class T has inherieted by class bar(something like this)
template <class T:public bar> void foo()
{
// do something
}
You can restrict T though using "Substitution Failure Is Not An Error" (SFINAE):
template <typename T>
typename std::enable_if<std::is_base_of<bar, T>::value>::type foo()
{
}
If T is not derived from bar, specialization of the function template will fail and it will not be considered during overload resolution. std::enable_if and std::is_base_of are new components of the C++ Standard Library added in the forthcoming revision, C++0x. If your compiler/Standard Library implementation don't yet support them, you can also find them in C++ TR1 or Boost.TypeTraits.
Yes, following technique can be used (for public inheritance). It will cause an overhead of just one pointer initialization.
Edit: Re-writing
template<typename Parent, typename Child>
struct IsParentChild
{
static Parent* Check (Child *p) { return p; }
Parent* (*t_)(Child*);
IsParentChild() : t_(&Check) {} // function instantiation only
};
template<typename T>
void foo ()
{
IsParentChild<Bar, T> check;
// ...
}