I try to write a class that takes a tuple of functions as its argument and overloads operator() for all argument_types of the function. Right now this looks like this:
template<typename T>
struct holder {
T t;
};
template<typename T, std::size_t i>
struct overload_helper : public holder<T>, public overload_helper<T, i - 1> {
overload_helper(T t) : holder<T>({t}) {};
typedef typename std::tuple_element<i - 1, T>::type inner_type;
typename inner_type::result_type operator()(typename inner_type::argument_type x) {
return std::get<i - 1>(holder<T>::t)(x); }
};
template<typename T>
struct overload_helper<T, 1> {
typedef typename std::tuple_element<0 ,T>::type inner_type;
typename inner_type::result_type operator()(typename inner_type::argument_type x) {
return std::get<0>(holder<T>::t)(x); }
};
template<typename T>
struct overload : public overload_helper<T, std::tuple_size<T>::value>
{
typedef void result_type;
overload(const T& t) : overload_helper<T, std::tuple_size<T>::value>(t) { }
};
int main() {
auto all = make_overload(std::make_tuple(
std::function<void(double)>([](double i) {
std::cout << "double" << std::endl;
}), std::function<void(int)>([](int i) {
std::cout << "int" << std::endl; })));
all(1); //fails
all(1.0);
}
The problem is that the base class is hiding each recursive definition of operator(). Is it possible to recursively unhide all definitions with using or is the only way to have a templated operator() and pick the right overload with boost::mpl?
using overload_helper<T, i - 1>::operator() in each overload_helper should do the job, so long they aren't ambiguous.
Related
I have a template class and a member function print() to print the data.
template<typename T>
class A
{
public:
T data;
void print(void)
{
std::cout << data << std::endl;
}
// other functions ...
};
Then, I want to either print scalar data or vector data, so I give a specialized definition and get a compiler error.
template<typename T>
void A<std::vector<T>>::print(void) // template argument list error
{
for (const auto& d : data)
{
std::cout << d << std::endl;
}
}
Question: Why does this member function specialization get an error? What is the correct way to define a print function for a vector?
Solution 1: I have tested the following definition.
template<typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print(void) { // OK
// ...
}
}
This one worked, but I have to copy the other member functions into this specialized class.
EDIT:
Solution 2: To prevent copy all the other member functions, I define a base class containing the common member functions and inherit from the base class:
template<typename T>
class Base
{
public:
T data;
// other functions ...
};
template<typename T>
class A : public Base<T>
{
public:
void print(void)
{
std::cout << this->data << std::endl;
}
};
template<typename T>
class A<std::vector<T>> : public Base<std::vector<T>>
{
public:
void print(void)
{
for (const auto& d : this->data)
{
std::cout << d << std::endl;
}
}
};
This solution works well. Are there some better or more conventional solutions?
Why does this member function specialization get error?
When you instantiate the template class A for example A<std::vector<int>>, the template parameter T is equal to std::vector<int>, not std::vector<T>, and this a specialization case of the function. Unfortunately this can not be done with member functions as mentioned in the comments.
Are there some better solutions?
Yes; In c++17 you could use if constexpr with a trait to check the std::vector, like this.
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
void print() const /* noexcept */
{
if constexpr (is_std_vector<T>::value) // when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else // for types other than `std::vector<>`
{
std::cout << mData << std::endl;
}
}
};
(See Live Online)
This way you keep only one template class and the print() will instantiate the appropriate part according to the template type T at compile time.
If you don not have access to C++17, other option is to SFINAE the members(Since c++11).
#include <type_traits> // std::false_type, std::true_type, std::enbale_if
#include <vector>
// traits for checking wether T is a type of std::vector<>
template<typename T> struct is_std_vector final : std::false_type {};
template<typename... T> struct is_std_vector<std::vector<T...>> final : std::true_type {};
template<typename T>
class A /* final */
{
T mData;
public:
// ...constructor
template<typename Type = T> // when T == `std::vector<>`
auto print() const -> typename std::enable_if<is_std_vector<Type>::value>::type
{
for (const auto element : mData)
std::cout << element << "\n";
}
template<typename Type = T> // for types other than `std::vector<>`
auto print() const -> typename std::enable_if<!is_std_vector<Type>::value>::type
{
std::cout << mData << std::endl;
}
};
(See Live Online)
What if I have more other data types like self-define vector classes
or matrices? Do I have to define many is_xx_vector?
You can check the type is a specialization of the provided one like as follows. This way you can avoid providing many traits for each type. The is_specialization is basically inspired from this post
#include <type_traits> // std::false_type, std::true_type
#include <vector>
// custom MyVector (An example)
template<typename T> struct MyVector {};
template<typename Test, template<typename...> class ClassType>
struct is_specialization final : std::false_type {};
template<template<typename...> class ClassType, typename... Args>
struct is_specialization<ClassType<Args...>, ClassType> final : std::true_type {};
And the print function could be in c++17:
void print() const /* noexcept */
{
if constexpr (is_specialization<T, std::vector>::value)// when T == `std::vector<>`
{
for (const auto element : mData)
std::cout << element << "\n";
}
else if constexpr (is_specialization<T, ::MyVector>::value) // custom `MyVector`
{
std::cout << "MyVector\n";
}
else // for types other than `std::vector<>` and custom `MyVector`
{
std::cout << mData << std::endl;
}
}
(See Live Online)
You need to implement a template class that uses a vector as template parameter. This worked for me.
template<typename T>
class A
{
public:
T data;
void print(void) {
std::cout << "Data output" << std::endl;
}
// other functions ...
};
template <typename T>
class A<std::vector<T>>
{
public:
std::vector<T> data;
void print() {
for (auto i : data) {
std::cout << "Vector output" << std::endl;
}
}
};
You could always use named tag dispatching to check if type provided by template user is vector.
A<std::vector<T>> notation won't work as you both try to take into account that T is type and vector of types which is contradicting with itself.
Below is code I used named tag dispatching as solution to your problem:
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
template<typename T> struct is_vector : public std::false_type {};
template<typename T, typename A>
struct is_vector<std::vector<T, A>> : public std::true_type {};
template<typename T>
class A
{
public:
T data;
void print(std::true_type) {
for (auto& a : data) { std::cout << a << std::endl; }
}
void print(std::false_type) {
std::cout << data << std::endl;
}
void print() {
print(is_vector<T>{});
}
};
int main()
{
A<int> a;
a.data = 1;
a.print();
A<std::vector<int>> b;
b.data = { 1, 2 ,3 ,4 ,5 };
b.print();
return 0;
}
Succesfully compiled with https://www.onlinegdb.com/online_c++_compiler
Based on answer: Check at compile-time is a template type a vector
You can dispatch printing to another member function (static or not). For example:
template<typename T>
class A {
public:
T data;
void print() const {
print_impl(data);
}
private:
template<class S>
static void print_impl(const S& data) {
std::cout << data;
}
template<class S, class A>
static void print_impl(const std::vector<S, A>& data) {
for (const auto& d : data)
std::cout << d;
}
};
I have this code:
template<class T1, class T2>
class Pair
{
private:
T1 first;
T2 second;
public:
void SetFirst(T1 first)
{
this.first = first;
}
void SetSecond(T2 second)
{
this.second = second;
}
T1 GetFirst()
{
return first;
}
T2 GetSecond()
{
return second;
}
};
How could I implement two single methods SetValue() and GetValue(), instead of the four I have, that decides depending on parameters which generic type that should be used? For instance I'm thinking the GetValue() method could take an int parameter of either 1 or 2 and depending on the number, return either a variable of type T1 or T2. But I don't know the return type beforehand so is there anyway to solve this?
Not sure to understand what do you want and not exactly what you asked but...
I propose the use of a wrapper base class defined as follows
template <typename T>
class wrap
{
private:
T elem;
public:
void set (T const & t)
{ elem = t; }
T get () const
{ return elem; }
};
Now your class can be defined as
template <typename T1, typename T2>
struct Pair : wrap<T1>, wrap<T2>
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
};
or, if you can use C++11 and variadic templates and if you define a type traits getType to get the Nth type of a list,
template <std::size_t I, typename, typename ... Ts>
struct getType
{ using type = typename getType<I-1U, Ts...>::type; };
template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
{ using type = T; };
you can define Pair in a more flexible way as follows
template <typename ... Ts>
struct Pair : wrap<Ts>...
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <std::size_t N, typename T>
void set (T const & t)
{ wrap<typename getType<N, Ts...>::type>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
template <std::size_t N>
typename getType<N, Ts...>::type get ()
{ return wrap<typename getType<N, Ts...>::type>::get(); }
};
Now the argument of set() can select the correct base class and the correct base element
Pair<int, long> p;
p.set(0); // set the int elem
p.set(1L); // set the long elem
otherwise, via index, you can write
p.set<0U>(3); // set the 1st (int) elem
p.set<1U>(4); // set the 2nd (long) elem
Unfortunately, the get() doesn't receive an argument, so the type have to be explicited (via type or via index)
p.get<int>(); // get the int elem value
p.get<long>(); // get the long elem value
p.get<0U>(); // get the 1st (int) elem value
p.get<1U>(); // get the 2nd (long) elem value
Obviously, this didn't work when T1 is equal to T2
The following is a (C++11) full working example
#include <iostream>
template <std::size_t I, typename, typename ... Ts>
struct getType
{ using type = typename getType<I-1U, Ts...>::type; };
template <typename T, typename ... Ts>
struct getType<0U, T, Ts...>
{ using type = T; };
template <typename T>
class wrap
{
private:
T elem;
public:
void set (T const & t)
{ elem = t; }
T get () const
{ return elem; }
};
template <typename ... Ts>
struct Pair : wrap<Ts>...
{
template <typename T>
void set (T const & t)
{ wrap<T>::set(t); }
template <std::size_t N, typename T>
void set (T const & t)
{ wrap<typename getType<N, Ts...>::type>::set(t); }
template <typename T>
T get () const
{ return wrap<T>::get(); }
template <std::size_t N>
typename getType<N, Ts...>::type get ()
{ return wrap<typename getType<N, Ts...>::type>::get(); }
};
int main()
{
//Pair<int, int> p; compilation error
Pair<int, long, long long> p;
p.set(0);
p.set(1L);
p.set(2LL);
std::cout << p.get<int>() << std::endl; // print 0
std::cout << p.get<long>() << std::endl; // print 1
std::cout << p.get<long long>() << std::endl; // print 2
p.set<0U>(3);
p.set<1U>(4);
p.set<2U>(5);
std::cout << p.get<0U>() << std::endl; // print 3
std::cout << p.get<1U>() << std::endl; // print 4
std::cout << p.get<2U>() << std::endl; // print 5
}
C++ is statically typed, so the argument given must be a template-argument instead a function-argument.
And while it will look like just one function each to the user, it's really two.
template <int i = 1> auto GetValue() -> std::enable_if_t<i == 1, T1> { return first; }
template <int i = 2> auto GetValue() -> std::enable_if_t<i == 2, T2> { return second; }
template <int i = 1> auto SetValue(T1 x) -> std::enable_if_t<i == 1> { first = x; }
template <int i = 2> auto SetValue(T2 x) -> std::enable_if_t<i == 2> { second = x; }
I use SFINAE on the return-type to remove the function from consideration unless the template-argument is right.
For this particular situation, you should definitely prefer std::pair or std::tuple.
You can simply overload SetValue() (provided T1 and T2 can be distinguished, if not you have a compile error):
void SetValue(T1 x)
{ first=x; }
void SetValue(T2 x)
{ second=x; }
Then, the compiler with find the best match for any call, i.e.
Pair<int,double> p;
p.SetValue(0); // sets p.first
p.SetValue(0.0); // sets p.second
With GetValue(), the information of which element you want to retrieve cannot be inferred from something like p.GetValue(), so you must provide it somehow. There are several options, such as
template<typename T>
std::enable_if_t<std::is_same<T,T1>,T>
GetValue() const
{ return first; }
template<typename T>
std::enable_if_t<std::is_same<T,T2>,T>
GetValue() const
{ return second; }
to be used like
auto a = p.GetValue<int>();
auto b = p.GetValue<double>();
but your initial version is good enough.
Consider the following simplified piece of code for a variant class. Most of it is for informational purposes, the question is about the conditional_invoke method.
// Possible types in variant.
enum class variant_type { empty, int32, string };
// Actual data store.
union variant_data {
std::int32_t val_int32;
std::string val_string;
inline variant_data(void) { /* Leave uninitialised */ }
inline ~variant_data(void) { /* Let variant do clean up. */ }
};
// Type traits which allow inferring which type to use (these are actually generated by a macro).
template<variant_type T> struct variant_type_traits { };
template<class T> struct variant_reverse_traits { };
template<> struct variant_type_traits<variant_type::int32> {
typedef std::int32_t type;
inline static type *get(variant_data& d) { return &d.val_int32; }
};
template<> struct variant_reverse_traits<std::int32_t> {
static const variant_type type = variant_type::int32;
inline static std::int32_t *get(variant_data& d) { return &d.val_int32; }
};
template<> struct variant_type_traits<variant_type::string> {
typedef std::string type;
inline static type *get(variant_data& d) { return &d.val_string; }
};
template<> struct variant_reverse_traits<std::string> {
static const variant_type type = variant_type::string;
inline static std::string *get(variant_data& d) { return &d.val_string; }
};
// The actual variant class.
class variant {
public:
inline variant(void) : type(variant_type::empty) { }
inline ~variant(void) {
this->conditional_invoke<destruct>();
}
template<class T> inline variant(const T value) : type(variant_type::empty) {
this->set<T>(value);
}
template<class T> void set(const T& value) {
this->conditional_invoke<destruct>();
std::cout << "Calling data constructor ..." << std::endl;
::new (variant_reverse_traits<T>::get(this->data)) T(value);
this->type = variant_reverse_traits<T>::type;
}
variant_data data;
variant_type type;
private:
template<variant_type T> struct destruct {
typedef typename variant_type_traits<T>::type type;
static void invoke(type& v) {
std::cout << "Calling data destructor ..." << std::endl;
v.~type();
}
};
template<template<variant_type> class F, class... P>
inline void conditional_invoke(P&&... params) {
this->conditional_invoke0<F, variant_type::int32, variant_type::string, P...>(std::forward<P>(params)...);
}
template<template<variant_type> class F, variant_type T, variant_type... U, class... P>
void conditional_invoke0(P&&... params) {
if (this->type == T) {
F<T>::invoke(*variant_type_traits<T>::get(this->data), std::forward<P>(params)...);
}
this->conditional_invoke0<F, U..., P...>(std::forward<P>(params)...);
}
template<template<variant_type> class F, class... P>
inline void conditional_invoke0(P&&... params) { }
};
The code works this way, i.e. it works as long as the parameter list P... for the functor is empty. If I add another functor like
template<variant_type T> struct print {
typedef typename variant_type_traits<T>::type type;
static void invoke(type& v, std::ostream& stream) {
stream << v;
}
};
and try to invoke it
friend inline std::ostream& operator <<(std::ostream& lhs, variant& rhs) {
rhs.conditional_invoke<print>(lhs);
return lhs;
}
the compiler VS 20115 complains
error C2672: 'variant::conditional_invoke0': no matching overloaded function found
or gcc respectively
error: no matching function for call to 'variant::conditional_invoke0 >&>(std::basic_ostream&)'
I guess the compiler cannot decide when U... ends and when P... starts. Is there any way to work around the issue?
You'll have to make both parameter packs deducible. That is, let the type and non-type template parameters be part of a function parameter list. For that, introduce a dummy structure:
template <variant_type...>
struct variant_type_list {};
and let the compiler deduce the variant_type... pack from a function call:
template <template <variant_type> class F
, variant_type T
, variant_type... U
, typename... P>
void conditional_invoke0(variant_type_list<T, U...> t
, P&&... params)
{
if (this->type == T)
{
F<T>::invoke(*variant_type_traits<T>::get(this->data)
, std::forward<P>(params)...);
}
this->conditional_invoke0<F>(variant_type_list<U...>{}
, std::forward<P>(params)...);
}
To break recursive calls, introduce an overload with an empty variant_type_list:
template <template <variant_type> class F, typename... P>
void conditional_invoke0(variant_type_list<>, P&&... params) {}
When calling the invoker for the first time, provide variant_types as an argument:
this->conditional_invoke0<F>(variant_type_list<variant_type::int32, variant_type::string>{}
, std::forward<P>(params)...);
DEMO
I have a templated class with variable numbers of templated arguments. As in these cases (I cannot afford C++11) a good practice is to create a default class that we call none and put it as default like below.
struct none {};
template<class T1=none, T2=none, T3=none>
class A{
template<class T>
double extract() { return none();}
template<>
double extract<T1>() { return m1_();}
template<>
double extract<T2>() { return m2_();}
template<>
double extract<T3> () { return m3_();}
T1 m1_;
T2 m2_;
T3 m3_;
};
At this stage I don't know how to implement a generic/templated accessor function that can access each of the templated argument.
All of the templated arguments are different so I specialized A::extract() for each of the templated arguments.
Is there any better way to do this? Any sort of tagging I can have a look at?
struct none {};
template <class T, class N>
class Holder : public N
{
protected:
T m;
typedef Holder<T, N> First;
double extractP(T*) { return m(); }
template <class X> double extractP(X*) {
return this->N::extractP(static_cast<X*>(0));
}
};
template <class T>
class Holder<T, none>
{
protected:
T m;
typedef Holder<T, none> First;
double extractP(T*) { return m(); }
template <class X> none extractP(X*) {
return none();
}
};
template <class T1 = none, class T2 = none, class T3 = none>
class A : Holder<T1, Holder<T2, Holder<T3, none> > >
{
public:
template <class T> double extract() {
return this->extractP(static_cast<T*>(0));
}
};
A similarly-named solution to n.m but more on the Boost's Variant class design.
The suggestion is to use a Variant container (a generic container for your objects) and use accessors directly on them.
#include <iostream>
#include <stdexcept>
using namespace std;
class BaseHolder
{
public:
virtual ~BaseHolder(){}
virtual BaseHolder* clone() const = 0;
};
template<typename T>
class HoldData : public BaseHolder
{
public:
HoldData(const T& t_) : t(t_){}
virtual BaseHolder* clone() const {
return new HoldData<T>(t);
}
T getData() {
return t;
}
private:
T t;
};
class Variant
{
public:
Variant() : data(0) {}
template<typename T>
Variant(const T& t) : data(new HoldData<T>(t)){}
Variant(const Variant& other) : data(other.data ? other.data->clone() : 0) {}
~Variant(){delete data;}
template<typename T>
T getData() {
return ((HoldData<T>*)data)->getData();
}
private:
BaseHolder* data;
private:
Variant& operator=(const Variant& other) { return *this;} // Not allowed
};
struct none {};
class Container{
public:
Container() : m1_(0), m2_(0), m3_(0){}
~Container() {
if(m1_)
delete m1_;
if(m2_)
delete m1_;
if(m3_)
delete m1_;
}
none extract() { return none();}
template<typename T>
void insertM1(T obj) {
m1_ = new Variant(obj);
}
template<typename T>
T extractM1() {
if(m1_ != 0)
return m1_->getData<T>();
else
throw std::runtime_error("Element not set");
}
// TODO: implement m2 and m3
Variant *m1_;
Variant *m2_;
Variant *m3_;
};
int main() {
Container obj;
char M1 = 'Z';
obj.insertM1(M1);
char extractedM1 = obj.extractM1<char>();
cout << extractedM1;
return 0;
}
http://ideone.com/BaCWSV
Your class seems to mimic std::tuple, which, unfortunately for you, was added in C++11. The good news is that you can use boost::tuple instead.
As an example of usage:
boost::tuple<std::string, double> t = boost::make_tuple("John Doe", 4.815162342);
std::cout << boost::get<0>(t) << '\n';
std::cout << boost::get<1>(t) << '\n';
Live demo
Without access to C++11, it's a bit uglier, but you can leverage Boost.Tuple:
#include <iostream>
#include <boost/tuple/tuple.hpp>
template <size_t I, typename T, typename U>
struct AccessImpl;
template <size_t I, typename T, typename U>
struct AccessImpl {
template <typename Tuple>
static T& impl(Tuple& tuple) {
typedef typename ::boost::tuples::element<I+1, Tuple>::type Next;
return AccessImpl<I+1, T, Next>::impl(tuple);
}
};
template <size_t I, typename T>
struct AccessImpl<I, T, T> {
template <typename Tuple>
static T& impl(Tuple& tuple) { return boost::get<I>(tuple); }
};
template <typename T, typename Tuple>
T& access(Tuple& tuple) {
typedef typename ::boost::tuples::element<0, Tuple>::type Head;
return AccessImpl<0, T, Head>::impl(tuple);
}
int main() {
boost::tuples::tuple<char, int, std::string> example('a', 1, "Hello, World!");
std::cout << access<std::string>(example) << "\n";
return 0;
}
This, as expected, prints "Hello, World!".
I need to create a template function like this:
template<typename T>
void foo(T a)
{
if (T is a subclass of class Bar)
do this
else
do something else
}
I can also imagine doing it using template specialization ... but I have never seen a template specialization for all subclasses of a superclass. I don't want to repeat specialization code for each subclass
You can do what you want but not how you are trying to do it! You can use std::enable_if together with std::is_base_of:
#include <iostream>
#include <utility>
#include <type_traits>
struct Bar { virtual ~Bar() {} };
struct Foo: Bar {};
struct Faz {};
template <typename T>
typename std::enable_if<std::is_base_of<Bar, T>::value>::type
foo(char const* type, T) {
std::cout << type << " is derived from Bar\n";
}
template <typename T>
typename std::enable_if<!std::is_base_of<Bar, T>::value>::type
foo(char const* type, T) {
std::cout << type << " is NOT derived from Bar\n";
}
int main()
{
foo("Foo", Foo());
foo("Faz", Faz());
}
Since this stuff gets more wide-spread, people have discussed having some sort of static if but so far it hasn't come into existance.
Both std::enable_if and std::is_base_of (declared in <type_traits>) are new in C++2011. If you need to compile with a C++2003 compiler you can either use their implementation from Boost (you need to change the namespace to boost and include "boost/utility.hpp" and "boost/enable_if.hpp" instead of the respective standard headers). Alternatively, if you can't use Boost, both of these class template can be implemented quite easily.
I would use std::is_base_of along with local class as :
#include <type_traits> //you must include this: C++11 solution!
template<typename T>
void foo(T a)
{
struct local
{
static void do_work(T & a, std::true_type const &)
{
//T is derived from Bar
}
static void do_work(T & a, std::false_type const &)
{
//T is not derived from Bar
}
};
local::do_work(a, std::is_base_of<Bar,T>());
}
Please note that std::is_base_of derives from std::integral_constant, so an object of former type can implicitly be converted into an object of latter type, which means std::is_base_of<Bar,T>() will convert into std::true_type or std::false_type depending upon the value of T. Also note that std::true_type and std::false_type are nothing but just typedefs, defined as:
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
I know this question has been answered but nobody mentioned that std::enable_if can be used as a second template parameter like this:
#include <type_traits>
class A {};
class B: public A {};
template<class T, typename std::enable_if<std::is_base_of<A, T>::value, int>::type = 0>
int foo(T t)
{
return 1;
}
I like this clear style:
void foo_detail(T a, const std::true_type&)
{
//do sub-class thing
}
void foo_detail(T a, const std::false_type&)
{
//do else
}
void foo(T a)
{
foo_detail(a, std::is_base_of<Bar, T>::value);
}
The problem is that indeed you cannot do something like this in C++17:
template<T>
struct convert_t {
static auto convert(T t) { /* err: no specialization */ }
}
template<T>
struct convert_t<T> {
// T should be subject to the constraint that it's a subclass of X
}
There are, however, two options to have the compiler select the correct method based on the class hierarchy involving tag dispatching and SFINAE.
Let's start with tag dispatching. The key here is that tag chosen is a pointer type. If B inherits from A, an overload with A* is selected for a value of type B*:
#include <iostream>
#include <type_traits>
struct type_to_convert {
type_to_convert(int i) : i(i) {};
type_to_convert(const type_to_convert&) = delete;
type_to_convert(type_to_convert&&) = delete;
int i;
};
struct X {
X(int i) : i(i) {};
X(const X &) = delete;
X(X &&) = delete;
public:
int i;
};
struct Y : X {
Y(int i) : X{i + 1} {}
};
struct A {};
template<typename>
static auto convert(const type_to_convert &t, int *) {
return t.i;
}
template<typename U>
static auto convert(const type_to_convert &t, X *) {
return U{t.i}; // will instantiate either X or a subtype
}
template<typename>
static auto convert(const type_to_convert &t, A *) {
return 42;
}
template<typename T /* requested type, though not necessarily gotten */>
static auto convert(const type_to_convert &t) {
return convert<T>(t, static_cast<T*>(nullptr));
}
int main() {
std::cout << convert<int>(type_to_convert{5}) << std::endl;
std::cout << convert<X>(type_to_convert{6}).i << std::endl;
std::cout << convert<Y>(type_to_convert{6}).i << std::endl;
std::cout << convert<A>(type_to_convert{-1}) << std::endl;
return 0;
}
Another option is to use SFINAE with enable_if. The key here is that while the snippet in the beginning of the question is invalid, this specialization isn't:
template<T, typename = void>
struct convert_t {
static auto convert(T t) { /* err: no specialization */ }
}
template<T>
struct convert_t<T, void> {
}
So our specializations can keep a fully generic first parameter as long we make sure only one of them is valid at any given point. For this, we need to fashion mutually exclusive conditions. Example:
template<typename T /* requested type, though not necessarily gotten */,
typename = void>
struct convert_t {
static auto convert(const type_to_convert &t) {
static_assert(!sizeof(T), "no conversion");
}
};
template<>
struct convert_t<int> {
static auto convert(const type_to_convert &t) {
return t.i;
}
};
template<typename T>
struct convert_t<T, std::enable_if_t<std::is_base_of_v<X, T>>> {
static auto convert(const type_to_convert &t) {
return T{t.i}; // will instantiate either X or a subtype
}
};
template<typename T>
struct convert_t<T, std::enable_if_t<std::is_base_of_v<A, T>>> {
static auto convert(const type_to_convert &t) {
return 42; // will instantiate either X or a subtype
}
};
template<typename T>
auto convert(const type_to_convert& t) {
return convert_t<T>::convert(t);
}
Note: the specific example in the text of the question can be solved with constexpr, though:
template<typename T>
void foo(T a) {
if constexpr(std::is_base_of_v<Bar, T>)
// do this
else
// do something else
}
If you are allowed to use C++20 concepts, all this becomes almost trivial:
template<typename T> concept IsChildOfX = std::is_base_of<X, T>::value;
// then...
template<IsChildOfX X>
void somefunc( X& x ) {...}