I'm trying to override a C++ template for a specific type, which implements a member function. It seems to me, that this is not possible with the current language spec.
Overriding just works, if I use specialization for one specific type.
The code always prints World instead of Hello.
Is there any way to tell the compiler to use the other template?
I cant modify the first template of serialize() because it is part of an external library. I can just add additional templates.
#include <iostream>
#include <type_traits>
#include <new>
struct PanelBarcode
{
std::string value;
template<class S> void Serialize(S& in_s)
{
std::cout << value;
}
};
template <typename Archive, typename C>
struct has_serialize
{
private:
template<typename T>
static constexpr auto check(T*)
-> typename
std::is_same<
decltype(std::declval<T>().Serialize(std::declval<Archive&>())),
void
>::type;
template<typename>
static constexpr std::false_type check(...);
typedef decltype(check<C>(0)) type;
public:
static constexpr bool value = type::value;
};
struct X {
X() {}
template<class S> void Serialize(S& in_s)
{ }
};
template<class A, class T>
void serialize(A ar, T& obj, const unsigned int version)
{
std::cout << "World";
}
template<class A, class T, typename std::enable_if_t<has_serialize<A, T>::value> * = nullptr>
void serialize(A ar, T& obj, const unsigned int version)
{
obj.Serialize(ar);
}
struct Y
{
Y() {}
};
int main()
{
PanelBarcode bc;
bc.value = "Hello";
Y y;
serialize<Y, PanelBarcode>(y, bc, 13);
return 0;
}
Related
I am trying to understand the name lookup and argument dependency lookup.I have created a small example.
Edited:
https://godbolt.org/g/rMWUbe
#include <iostream>
void g(const int*) {}
template <typename T>
struct TypeResolution;
template <typename T>
struct TypeResolution<T&> {
typedef const T* type;
static constexpr void (*func_ptr)(TypeResolution::type) = g;
static constexpr void *func_ptr_void = (void*)func_ptr;
};
template <typename T>
struct TypeResolution {
typedef const T* type;
static constexpr void (*func_ptr)(TypeResolution::type) = g;
static constexpr void *func_ptr_void = (void*)func_ptr;
};
void foo_impl(void *[], void *[]) {
//Some work here, that will be in a different file or library
}
template <typename... ARGS>
void foo(ARGS && ... args) {
void *func_ptrs[] = { TypeResolution<ARGS>::func_ptr_void... };
void *args_ptrs[] = {(void*)&args...};
foo_impl(func_ptrs, args_ptrs);
}
struct MyClass {};
void g(const MyClass*) {}
int main(int argc, char* argv[]) {
int i = 1;
foo(i);
int j = 2;
foo(i, j);
MyClass c;
foo(c); //This fails.
}
So my question is, why it doesn't compile? Or more simply why the lookup of g inside the TypeResolution happens when the class is declared, and not when it is instantiated? As I expected inside the main function and then to see the function void g(const MyClass*)
What I want to obtain is to be able to call different functions for different different types, but without needing to forward declare them.
I am using g++ 5.4.0 on Ubuntu 16.04
template <typename T>
void foo(T t) {
void(*f)(T) = +[](T x){ return g(std::forward<T>(x)); };
f(t); //This compiles
g(t); //This compiles
}
this may be what you want. Note that the argument is moved once within f, which should not matter for primitive types.
template <class T>
struct TypeResolution;
template <class T>
struct TypeResolution<T&>:TypeResolution<T> {};
template <class T>
struct TypeResolution {
typedef const T* type;
static constexpr void (*func_ptr)(type) = +[](type x){ return g(x); };
static constexpr void *func_ptr_void = (void*)func_ptr;
};
requires c++17 because you cannot use lambdas in c++14 or c++11 in a constexpr expression.
In previous versions of C++ we can do:
template <class T>
struct TypeResolution {
typedef const T* type;
static void invoke_g(type t) { g( t ); }
static constexpr void (*func_ptr)(type) = invoke_g;
static constexpr void *func_ptr_void = (void*)func_ptr;
};
Based on Yakk's answer and based on n4487 I managed to implement a solution that I managed to compile with c++14.
https://godbolt.org/g/BRee4u
#include <iostream>
#include <type_traits>
#include <string>
void g(int*) {}
template <typename T>
struct TypeResolution;
template <typename T>
struct TypeResolution<T&> {
struct Inner {
static constexpr void foo(T* t) { g(t); }
};
static constexpr void *func_ptr_void = (void*)Inner::foo;
};
template <typename T>
struct TypeResolution {
struct Inner {
static constexpr void foo(T* t) { g(t); }
};
static constexpr void *func_ptr_void = (void*)Inner::foo;
};
void foo_impl(void *func_args[], void *data_args[]) {
//Some work here, that will be in a different file or library
}
template <typename... ARGS>
void foo(ARGS && ... args) {
void *func_ptrs[] = { TypeResolution<ARGS>::func_ptr_void... };
void *args_ptrs[] = {(void*)&args...};
foo_impl(func_ptrs, args_ptrs);
}
struct MyClass {};
void g(const MyClass*) {}
int main(int argc, char* argv[]) {
int i = 1;
foo(i);
MyClass c;
foo(c);
}
I am trying to use boost fusion for one of my projects and I an figuring out how to get type names and variable names for structures and classes.
#include <typeinfo>
#include <string>
#include <iostream>
#include <boost/fusion/include/sequence.hpp>
#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/lexical_cast.hpp>
using namespace boost::fusion;
struct Foo
{
int integer_value;
bool boolean_value;
};
class Bar
{
int integer_value;
bool boolean_value;
public:
Bar(int i_val, bool b_val):integer_value(i_val),boolean_value(b_val) {}
int get_integer_value() const { return integer_value; }
void set_integer_value(int i_val) { integer_value = i_val; }
bool get_boolean_value() const { return boolean_value; }
void set_boolean_value(bool b_val) { boolean_value = b_val; }
};
BOOST_FUSION_ADAPT_STRUCT(
Foo,
(int, integer_value)
(bool, boolean_value)
)
BOOST_FUSION_ADAPT_ADT(
Bar,
(int, int, obj.get_integer_value() , obj.set_integer_value(val))
(bool, bool, obj.get_boolean_value(), obj.set_boolean_value(val))
)
struct DisplayMembers
{
template <typename T>
void operator()(T& t) const {
std::cout << typeid(t).name() << " : " << boost::lexical_cast<std::string>(t) << std::endl;
}
};
int main(int argc, char *argv[])
{
struct Foo f = { 33, false};
for_each(f, DisplayMembers());
Bar b(34,true);
for_each(b, DisplayMembers());
return 0;
}
In the above example the result is
int : 33
bool : 0
struct boost::fusion::extension::adt_attribute_proxy<class Bar,0,0> : 34
struct boost::fusion::extension::adt_attribute_proxy<class Bar,1,0> : 1
I want the result as
int : integer_value : 33
bool : boolean_value : 0
int : integer_value : 34
bool : boolean_value : 1
I distilled the answer by sehe into something much simpler, provided you are using C++14
#include <iostream>
#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/mpl/range_c.hpp>
struct MyStruct {
std::string foo;
double bar;
};
BOOST_FUSION_ADAPT_STRUCT(MyStruct,
foo,
bar)
namespace fuz = boost::fusion;
namespace mpl = boost::mpl;
int main(int argc, char* argv[]) {
MyStruct dummy{"yo",3.14};
fuz::for_each(mpl::range_c<
unsigned, 0, fuz::result_of::size<MyStruct>::value>(),
[&](auto index){
std::cout << "Name: "
<< fuz::extension::struct_member_name<MyStruct,index>::call()
<< " Value: "
<< fuz::at_c<index>(dummy) << std::endl;
});
}
Outputs:
Name: foo Value: yo
Name: bar Value: 3.14
See it live on coliru
There's boost::fusion::extension::struct_member_name<S, N::value> to access the names.
Here's a generic fusion object visitor that I use:
namespace visitor {
template <typename Flavour, typename T> struct VisitorApplication;
namespace detail
{
template <typename V, typename Enable = void>
struct is_vector : boost::mpl::false_ { };
template <typename T>
struct is_vector<std::vector<T>, void> : boost::mpl::true_ { };
namespace iteration
{
// Iteration over a sequence
template <typename FusionVisitorConcept, typename S, typename N>
struct members_impl
{
// Type of the current member
typedef typename boost::fusion::result_of::value_at<S, N>::type current_t;
typedef typename boost::mpl::next<N>::type next_t;
typedef boost::fusion::extension::struct_member_name<S, N::value> name_t;
static inline void handle(FusionVisitorConcept& visitor, const S& s)
{
visitor.start_member(name_t::call());
VisitorApplication<FusionVisitorConcept, current_t>::handle(visitor, boost::fusion::at<N>(s));
visitor.finish_member(name_t::call());
members_impl<FusionVisitorConcept, S, next_t>::handle(visitor, s);
}
};
// End condition of sequence iteration
template <typename FusionVisitorConcept, typename S>
struct members_impl<FusionVisitorConcept, S, typename boost::fusion::result_of::size<S>::type>
{
static inline void handle(FusionVisitorConcept const&, const S&) { /*Nothing to do*/ }
};
// Iterate over struct/sequence. Base template
template <typename FusionVisitorConcept, typename S>
struct Struct : members_impl<FusionVisitorConcept, S, boost::mpl::int_<0>> {};
} // iteration
template <typename FusionVisitorConcept, typename T>
struct array_application
{
typedef array_application<FusionVisitorConcept, T> type;
typedef typename T::value_type value_type;
static inline void handle(FusionVisitorConcept& visitor, const T& t)
{
visitor.empty_array();
for (auto& el : t)
VisitorApplication<FusionVisitorConcept, value_type>::handle(visitor, el);
}
};
template <typename FusionVisitorConcept, typename T>
struct struct_application
{
typedef struct_application<FusionVisitorConcept, T> type;
static inline void handle(FusionVisitorConcept& visitor, const T& t)
{
visitor.empty_object();
iteration::Struct<FusionVisitorConcept, T>::handle(visitor, t);
}
};
template <typename FusionVisitorConcept, typename T, typename Enable = void>
struct value_application
{
typedef value_application<FusionVisitorConcept, T> type;
static inline void handle(FusionVisitorConcept& visitor, const T& t) {
visitor.value(t);
}
};
template <typename FusionVisitorConcept, typename T>
struct value_application<FusionVisitorConcept, boost::optional<T> >
{
typedef value_application<FusionVisitorConcept, boost::optional<T> > type;
static inline void handle(FusionVisitorConcept& visitor, const boost::optional<T>& t) {
if (t)
VisitorApplication<FusionVisitorConcept, T>::handle(visitor, *t);
else
; // perhaps some default action?
}
};
template <typename FusionVisitorConcept, typename T>
struct select_application
{
typedef
//typename boost::mpl::eval_if<boost::is_array<T>, boost::mpl::identity<array_application<FusionVisitorConcept, T>>,
typename boost::mpl::eval_if<detail::is_vector<T>, boost::mpl::identity<array_application <FusionVisitorConcept, T>>,
typename boost::mpl::eval_if<boost::fusion::traits::is_sequence<T>, boost::mpl::identity<struct_application<FusionVisitorConcept, T>>,
boost::mpl::identity<value_application<FusionVisitorConcept, T>>
> >::type type;
};
} // detail
template <typename FusionVisitorConcept, typename T>
struct VisitorApplication : public detail::select_application<FusionVisitorConcept, T>::type
{
};
}
template <typename FusionVisitorConcept, typename T>
void apply_fusion_visitor(FusionVisitorConcept& visitor, T const& o)
{
visitor::VisitorApplication<FusionVisitorConcept, T>::handle(visitor, o);
}
You can use it by supplying a visitor, e.g. for xml-like output:
struct DisplayMemberVisitor {
typedef std::string result_type;
DisplayMemberVisitor() { ss << std::boolalpha; }
std::string complete() { return ss.str(); }
void start_member (const char* name) {
ss << "<" << name << ">";
}
void finish_member(const char* name) {
ss << "</" << name << ">";
}
template <typename T> void value(T const& value) {
ss << value;
}
void empty_object() { }
void empty_array() { }
private:
std::stringstream ss;
};
See it Live On Coliru where (including some debug output) it prints:
<integer_value>33</integer_value><boolean_value>false</boolean_value><integer_value>34</integer_value><boolean_value>true</boolean_value>
Note that the ADT adaptation macro doesn't include a name (because none is available). You can probably quite easily make a macro FUSION_ADAPT_KEYD_ADT that also accepts a name and generates the relevant specializations of boost::fusion::extension::struct_member_name.
BONUS MATERIAL
Adding member name traits to ADT adapted members
Here's a simplistic approach that shows what little amount of work needs to be done.
#define MY_ADT_MEMBER_NAME(CLASSNAME, IDX, MEMBERNAME) \
namespace boost { namespace fusion { namespace extension { \
template <> struct struct_member_name<CLASSNAME, IDX> { typedef char const *type; static type call() { return #MEMBERNAME; } \
}; } } }
MY_ADT_MEMBER_NAME(Bar, 0, integer_value)
MY_ADT_MEMBER_NAME(Bar, 1, boolean_value)
This defines a macro to avoid most of the repetition. If you are a BOOST_PP whizkid you could somehow weave this into an adt_ex.hpp¹ header of sorts, so you could instead say:
BOOST_FUSION_ADAPT_ADT(Bar, // NOTE THIS PSEUDO-CODE
(integer_value, int, int, obj.get_integer_value(), obj.set_integer_value(val))
(boolean_value, bool, bool, obj.get_boolean_value(), obj.set_boolean_value(val)))
For now here's the ADT adapted trick Live On Coliru
¹ in case you're interested, here's a tarball of a prepared adt_ex tree (drop in alongsize adt.hpp): adt_ex.tgz as a starting point. It's just adt* but with macros and header guards renamed to adt_ex*
template <typename T, typename C>
class CSVWriter{
template <typename PrinterT>
void write(std::ostream& stream, const PrinterT& printer){
}
};
I want to check whether there exists at least two overloads PrinterT::operator()(T*) and PrinterT::operator()(C*)
PrinterT may or may not inherit from std::unary_function
What concept Checking Classes I need to use here ?
(I am not using C++11)
You can use something like that
#include <iostream>
#include <boost/concept/requires.hpp>
#include <boost/concept/usage.hpp>
template <class Type, class Param>
class has_operator_round_brackets_with_parameter
{
public:
BOOST_CONCEPT_USAGE(has_operator_round_brackets_with_parameter)
{
_t(_p);
}
private:
Type _t;
Param _p;
};
struct X {};
struct Y {};
struct Test1
{
void operator() (X*) const { }
};
struct Test2: public Test1
{
void operator() (X*) const { }
void operator() (Y*) const { }
};
template <class T, class C>
struct CSVWriter
{
template <class PrinterT>
BOOST_CONCEPT_REQUIRES(
((has_operator_round_brackets_with_parameter<PrinterT, T*>))
((has_operator_round_brackets_with_parameter<PrinterT, C*>)),
(void)) write(std::ostream& stream, const PrinterT& printer)
{
}
};
int main()
{
CSVWriter<X, Y> w;
// w.write<Test1>(std::cout, Test1()); // FAIL
w.write<Test2>(std::cout, Test2()); // OK
return 0;
}
Suppose I have a std::tuple made up of types like
struct A {
static void tip();
};
struct B {
static void tip();
};
struct Z {
};
std::tuple<const A&,const B&,const Z&> tpl;
Yes, I need separate A, B. (The implementation of ::tip() differs for each type.) What I try to implement is a type-sensitive "visitor" that iterates through the tuple starting from the beginning to the end. Upon visiting a particular element of type T a function should be called depending on whether T has the ::tip() method or not. In the simple example of above only A and B have ::tip() implemented and Z not. So, the iterator should call twice the function for types with the ::tip() method and once the other function.
Here is what I came up with:
template< int N , bool end >
struct TupleIter
{
template< typename T , typename... Ts >
typename std::enable_if< std::is_function< typename T::tip >::value , void >::type
static Iter( const T& dummy , const std::tuple<Ts...>& tpl ) {
std::cout << "tip\n";
std::get<N>(tpl); // do the work
TupleIter<N+1,sizeof...(Ts) == N+1>::Iter( std::get<N+1>(tpl) , tpl );
}
template< typename T , typename... Ts >
typename std::enable_if< ! std::is_function< typename T::tip >::value , void >::type
static Iter( const T& dummy , const std::tuple<Ts...>& tpl ) {
std::cout << "no tip\n";
std::get<N>(tpl); // do the work
TupleIter<N+1,sizeof...(Ts) == N+1>::Iter( std::get<N+1>(tpl) , tpl );
}
};
template< int N >
struct TupleIter<N,true>
{
template< typename T , typename... Ts >
static void Iter( const std::tuple<Ts...>& tpl ) {
std::cout << "end\n";
}
};
I use a dummy instance of the type of the element at the iterator position and decide via enable_if which function to call. Unfortunately this doesn't work/isn't a nice solution:
The compiler complains about recursive instantiation
The const T& dummy is not a clean solution
I was wondering if enable_if is the right strategy to do the decision and how can one recursively iterate through the std::tuple capturing the first type and keeping all the remaining arguments in vital state. Read through How to split a tuple? but it doesn't do any decision.
How can one implement such a thing in a correct and portable way in C++11?
Well, it was harder than I expected, but this works.
Some things you were doing wrong/that I modified:
You can't evaluate this: std::is_function< typename T::tip >::value, since T::tip is not a type. Even if this could be evaluated, what would happen when T::tip does not exist? Substitution would still fail.
Since you use const references as your tuple's inner types, you had to clean them before trying to find the tip member inside them. By cleaning I mean removing const and removing the reference.
That dummy type stuff was not a good idea, there was no need to use that parameter. You can achieve the same thing using std::tuple_element, which retrieves the i-th type from a tuple.
I modified TupleIter's template parameters to the following, which means:
"TupleIter that processes the index-th type, inside a tuple of size n".
template<size_t index, size_t n>
struct TupleIter;
The whole code is this:
#include <tuple>
#include <iostream>
#include <type_traits>
struct A {
static void tip();
};
struct B {
static void tip();
};
struct Z {
};
// Indicates whether the template parameter contains a static member named tip.
template<class T>
struct has_tip {
template<class U>
static char test(decltype(&U::tip));
template<class U>
static float test(...);
static const bool value = sizeof(test<typename std::decay<T>::type>(0)) == sizeof(char);
};
// Indicates whether the n-th type contains a tip static member
template<size_t n, typename... Ts>
struct nth_type_has_tip {
static const bool value = has_tip<typename std::tuple_element<n, std::tuple<Ts...>>::type>::value;
};
// Generic iteration
template<size_t index, size_t n>
struct TupleIter
{
template< typename... Ts >
typename std::enable_if< nth_type_has_tip<index, Ts...>::value , void >::type
static Iter(const std::tuple<Ts...>& tpl)
{
std::cout << "tip\n";
TupleIter<index + 1, n>::Iter(tpl );
}
template< typename... Ts >
typename std::enable_if< !nth_type_has_tip<index, Ts...>::value , void >::type
static Iter(const std::tuple<Ts...>& tpl) {
std::cout << "no tip\n";
TupleIter<index + 1, n>::Iter(tpl );
}
};
// Base class, we've reached the tuple end
template<size_t n>
struct TupleIter<n, n>
{
template<typename... Ts >
static void Iter( const std::tuple<Ts...>& tpl ) {
std::cout << "end\n";
}
};
// Helper function that forwards the first call to TupleIter<>::Iter
template<typename... Ts>
void iterate(const std::tuple<Ts...> &tup) {
TupleIter<0, sizeof...(Ts)>::Iter(tup);
}
int main() {
A a;
B b;
Z z;
std::tuple<const A&,const B&,const Z&> tup(a,b,z);
iterate(tup);
}
Here is another take on the question, very similar to mfontanini answer, but showcasing:
boost::fusion::for_each (instead of manually iterate over the tuple).
A variant for implementing has_type using an expression-based SFINAE approach, that I feel a little bit simpler to follow than the usual sizeof trick.
#include <boost/tuple/tuple.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
#include <boost/fusion/algorithm.hpp>
#include <iostream>
struct nat // not a type
{
private:
nat();
nat(const nat&);
nat& operator=(const nat&);
~nat();
};
template <typename T>
struct has_tip
{
static auto has_tip_imp(...) -> nat;
template <typename U>
static auto has_tip_imp(U&&) -> decltype(U::tip());
typedef decltype(has_tip_imp(std::declval<T>())) type;
static const bool value = !std::is_same<type, nat>::value;
};
struct CallTip
{
template<typename T>
typename std::enable_if<has_tip<T>::value>::type
operator()(T& t) const
{
std::cout << "tip\n";
T::tip();
}
template<typename T>
typename std::enable_if<!has_tip<T>::value>::type
operator()(T& t) const
{
std::cout << "no tip\n";
return;
}
};
struct A {
static void tip(){}
};
struct B {
static void tip(){}
};
struct Z {
};
int main()
{
A a;
B b;
Z z;
boost::tuple<const A&,const B&,const Z&> tpl(a, b, z);
boost::fusion::for_each(tpl, CallTip());
}
Note that if your compiler support variadic template you can use std::tuple instead of boost::tuple inside fusion::for_each by including #include<boost/fusion/adapted/std_tuple.hpp>
Edit :
As pointed by Xeo in the comment, it is possible to simplify a lot the expression-SFINAE approach by removing completely the trait has_tip and simply forward to a little call helper.
The final code is really neat and tight !
#include <boost/tuple/tuple.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
#include <boost/fusion/algorithm.hpp>
#include <iostream>
struct CallTip
{
template<typename T>
void operator()(const T& t) const
{
call(t);
}
template<class T>
static auto call(const T&) -> decltype(T::tip())
{
std::cout << "tip\n";
T::tip();
}
static void call(...)
{
std::cout << "no tip\n";
}
};
struct A {
static void tip(){}
};
struct B {
static void tip(){}
};
struct Z {
};
int main()
{
A a;
B b;
Z z;
boost::tuple<const A&,const B&,const Z&> tpl(a, b, z);
boost::fusion::for_each(tpl, CallTip());
}
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 ) {...}