Within a class, I have two different methods which should be mutually exclusive depending on the caller template parameter.
class Foo
{
// For collections
template<class T>
typename boost::enable_if<boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
doSomething()
{ }
// For single types
template<class T>
typename boost::enable_if<!boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
doSomething()
{ }
}
This won't compile.
error: type/value mismatch at argument 1 in template parameter list for 'template struct boost::enable_if'
error: expected a type, got '! boost::is_same::value'
How about:
template <typename T> struct is_std_vector : std::false_type {};
template <typename T, typename A>
struct is_std_vector<std::vector<T, A>> : std::true_type {};
And then
class Foo
{
// For collections
template<class T>
typename std::enable_if<is_std_vector<T>::value, const T&>::type
doSomething();
// For single types
template<class T>
typename std::enable_if<!is_std_vector<T>::value, const T&>::type
doSomething();
};
Unlike std's version, boost::enable_if accepts a type (kinda wrapper under boolean value), so you should write something like
class Foo
{
// For collections
template<class T>
typename boost::enable_if<
typename boost::is_same<typename std::vector<typename T::value_type>, T>,
const T&>::type doSomething()
{ }
// For single types
template<class T>
typename boost::enable_if_с<
!boost::is_same<typename std::vector<typename T::value_type>, T>::value,
const T&>::type doSomething()
{ }
}
Note here, I've used typename before boost::is_same and haven't used ::value in the first specification. On the contrary, I had to use enable_if_с in the second overload, because ! operator isn't applicable to a type.
What about a sort of tag dispatching?
#include <vector>
#include <iostream>
template <typename, typename>
struct isSame
{ typedef int type; };
template <typename T>
struct isSame<T, T>
{ typedef long type; };
struct foo
{
template <typename T>
T const & doSomething (T const & t, int)
{ std::cout << "int version" << std::endl; return t; }
template <typename T>
T const & doSomething (T const & t, long)
{ std::cout << "long version" << std::endl; return t; }
template <typename T>
T const & doSomething (T const & t)
{ return doSomething(t, typename isSame<
typename std::vector<typename T::value_type>, T>::type()); }
};
int main ()
{
foo f;
std::vector<int> v;
f.doSomething(v); // print "long version"
}
If what you want is to overload the function based on whether you are given a vector or not
#include <type_traits>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
class Foo {
public:
// For collections
template <class T>
const vector<T>& do_something(const std::vector<T>& input) {
cout << __PRETTY_FUNCTION__ << endl;
return input;
}
// For single types
template <class T>
const T& do_something(const T& input) {
cout << __PRETTY_FUNCTION__ << endl;
return input;
}
};
int main() {
auto foo = Foo{};
auto v = std::vector<int>{};
auto i = int{};
foo.do_something(v);
foo.do_something(i);
}
If you want to be even more general and check for any instantiated type
#include <type_traits>
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
namespace {
template <typename T, template <typename...> class TT>
struct IsInstantiationOf
: public std::integral_constant<bool, false> {};
template <template <typename...> class TT, typename... Args>
struct IsInstantiationOf<TT<Args...>, TT>
: public std::integral_constant<bool, true> {};
} // namespace anonymous
class Foo {
public:
// For collections
template <typename VectorType, typename std::enable_if_t<IsInstantiationOf<
std::decay_t<VectorType>, std::vector>::value>* = nullptr>
void do_something(VectorType&&) {
cout << "Vector overload" << endl;
}
// For single types
template <class T, typename std::enable_if_t<!IsInstantiationOf<
std::decay_t<T>, std::vector>::value>* = nullptr>
void do_something(T&&) {
cout << "Non vector overload" << endl;
}
};
int main() {
auto foo = Foo{};
auto v = std::vector<int>{};
auto i = int{};
foo.do_something(v);
foo.do_something(i);
}
Also please note that you should avoid putting std::enable_if in the function signature as much as possible for these reasons https://stackoverflow.com/a/14623831/5501675
Related
I have the following code which compiles nicely:
#include <iostream>
struct Res {};
struct Jac {};
template <typename T, typename S>
class A;
template <typename S>
class A<Res, S>
{
public:
A() { std::cout << "A<Res, S>" << std::endl; }
};
template <typename S>
class A<Jac, S>
{
public:
A() { std::cout << "A<Jac, S>" << std::endl; }
};
template <typename T, typename S>
class B;
template <typename S>
class B<Res, S>
{
public:
B() { std::cout << "B<Res, S>" << std::endl; }
};
template <typename S>
class B<Jac, S>
{
public:
B() { std::cout << "B<Jac, S>" << std::endl; }
};
template<typename S, typename EvalT,
std::enable_if_t<std::is_same<EvalT, A<Res,S>>::value, bool> = true
>
void foo()
{
A<Res, S> a_res;
A<Jac, S> a_jac;
}
template<typename S, typename EvalT,
std::enable_if_t<std::is_same<EvalT, B<Res,S>>::value, bool> = true
>
void foo()
{
B<Res, S> b_res;
B<Jac, S> b_jac;
}
int main() {
foo<int, A<Res,int>>();
foo<int, B<Res,int>>();
return 0;
}
However I am not happy with the calls inside my main() function. I would like them to look like this:
foo<int, A>();
foo<int, B>();
which would imply the following modification of the templates for foo():
template<typename S, typename EvalT,
std::enable_if_t<std::is_same<EvalT, B>::value, bool> = true
>
void foo()
{
B<Res, S> b_res;
B<Jac, S> b_jac;
}
This obviously does not compile. The idea is to have a function, which would instantiate either A or B without explicitly specifying T for my classes because I know that foo() has to create 2 instances with Res and Jac as type parameters. Is there any way to make the code neater and achieve such a behavior?
You can change foo to accept a template template parameter CT that is templated on two types, and enable_if the specific overload based on whether CT<Res, S> is the same type as A<Res, S>, or B<Res, S>:
template<typename S, template<typename, typename> typename CT,
std::enable_if_t<std::is_same<CT<Res,S>, A<Res,S>>::value, bool> = true
>
void foo()
{
A<Res, S> a_res;
A<Jac, S> a_jac;
}
template<typename S, template<typename, typename> typename CT,
std::enable_if_t<std::is_same<CT<Res, S>, B<Res,S>>::value, bool> = true
>
void foo()
{
B<Res, S> b_res;
B<Jac, S> b_jac;
}
Here's a demo.
The below code fails to compile. For some reason inheriting from HasFoo causes IsWrapper to fail. It has something to do with the friend function foo() because inheriting from other classes seems to work fine. I don't understand why inheriting from HasFoo causes the detection idiom to fail.
What is the proper way to detect WithFoo as a Wrapper?
https://godbolt.org/z/VPyarN
#include <type_traits>
#include <iostream>
template<typename TagType, typename ValueType>
struct Wrapper {
ValueType V;
};
// Define some useful metafunctions.
template<typename Tag, typename T>
T BaseTypeImpl(const Wrapper<Tag, T> &);
template<typename T>
using BaseType = decltype(BaseTypeImpl(std::declval<T>()));
template<typename Tag, typename T>
Tag TagTypeImpl(const Wrapper<Tag, T> &);
template<typename T>
using TagType = decltype(TagTypeImpl(std::declval<T>()));
// Define VoidT. Not needed with C++17.
template<typename... Args>
using VoidT = void;
// Define IsDetected.
template<template <typename...> class Trait, class Enabler, typename... Args>
struct IsDetectedImpl
: std::false_type {};
template<template <typename...> class Trait, typename... Args>
struct IsDetectedImpl<Trait, VoidT<Trait<Args...>>, Args...>
: std::true_type {};
template<template<typename...> class Trait, typename... Args>
using IsDetected = typename IsDetectedImpl<Trait, void, Args...>::type;
// Define IsWrapper true if the type derives from Wrapper.
template<typename T>
using IsWrapperImpl =
std::is_base_of<Wrapper<TagType<T>, BaseType<T>>, T>;
template<typename T>
using IsWrapper = IsDetected<IsWrapperImpl, T>;
// A mixin.
template<typename T>
struct HasFoo {
template<typename V,
typename std::enable_if<IsWrapper<T>::value &&
IsWrapper<V>::value>::type * = nullptr>
friend void foo(const T &This, const V &Other) {
std::cout << typeid(This).name() << " and " << typeid(Other).name()
<< " are wrappers\n";
}
};
template<typename Tag>
struct WithFoo : public Wrapper<WithFoo<Tag>, int>,
public HasFoo<WithFoo<Tag>> {};
int main(void) {
struct Tag {};
WithFoo<Tag> WrapperFooV;
// Fails. Why?
static_assert(IsWrapper<decltype(WrapperFooV)>::value,
"Not a wrapper");
return 0;
}
I don't understand why inheriting from HasFoo causes the detection idiom to fail.
Isn't completely clear to me also but surely a problem is that you use IsWrapper<T> inside the body of HasFoo<T> and, when you inherit HasFoo<WithFoo<Tag>> from WithFoo<Tag> you have that WithFoo<Tag> is incomplete when you check it with IsWrapper.
A possible solution (I don't know if acceptable for you) is define (and SFINAE enable/disable) foo() outside HasFoo.
I mean... try rewriting HasFoo as follows
template <typename T>
struct HasFoo {
template <typename V>
friend void foo(const T &This, const V &Other);
};
and defining foo() outside
template <typename T, typename V>
std::enable_if_t<IsWrapper<T>::value && IsWrapper<V>::value>
foo(const T &This, const V &Other) {
std::cout << typeid(This).name() << " and " << typeid(Other).name()
<< " are wrappers\n";
}
What is the proper way to detect WithFoo as a Wrapper?
Sorry but your code is too complicated for me.
I propose the following (simpler, I hope) alternative
#include <type_traits>
#include <iostream>
template<typename TagType, typename ValueType>
struct Wrapper {
ValueType V;
};
template <typename T1, typename T2>
constexpr std::true_type IW_helper1 (Wrapper<T1, T2> const &);
template <typename T>
constexpr auto IW_helper2 (T t, int) -> decltype( IW_helper1(t) );
template <typename T>
constexpr std::false_type IW_helper2 (T, long);
template <typename T>
using IsWrapper = decltype(IW_helper2(std::declval<T>(), 0));
template <typename T>
struct HasFoo {
template <typename V>
friend void foo(const T &This, const V &Other);
};
template <typename T, typename V>
std::enable_if_t<IsWrapper<T>::value && IsWrapper<V>::value>
foo(const T &This, const V &Other) {
std::cout << typeid(This).name() << " and " << typeid(Other).name()
<< " are wrappers\n";
}
template<typename Tag>
struct WithFoo : public Wrapper<WithFoo<Tag>, int>,
public HasFoo<WithFoo<Tag>> {};
int main () {
struct Tag {};
WithFoo<Tag> WrapperFooV;
static_assert(IsWrapper<decltype(WrapperFooV)>::value,
"Not a wrapper");
}
Here is my C++ program code
#include <iostream>
#include <memory>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <typename T>
void func(const A<T>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
}
and it prints 1. But what I expect is the function call prints 2. Why B<int>b matches to const T& instead of const A<T>&. And how can I make it to match to const A<T>&?
Solution 1
You could use std::enable_if and templated template parameters to get a better match for the function, like:
#include <iostream>
#include <memory>
#include <type_traits>
using namespace std;
template <typename T>
struct A { T x; };
template <typename T>
struct B:A<T> { };
template <template <typename...> typename T, typename ...Args, typename = std::enable_if_t<std::is_base_of_v<A<Args...>, T<Args...>>>>
void func(const T<Args...>& t) {
cout<<"2"<<endl;
}
template <typename T>
void func(const T& t) {
cout<<"1"<<endl;
}
int main() {
B<int> b;
func(b);
func(5);
}
However, this works only if A takes exactly the same template parameters as T. So if your B changes to ex.
template <typename T, typename U>
struct B : A<T> {}
this won't work anymore.
Solution 2
Based on Yakk's answer you can create a type trait that is more generic. This solution has no restrictions to its template parameters, like solution 1 does.
namespace detail
{
template <template <typename...> typename Base>
struct template_base_detector
{
template <typename... Args>
constexpr std::true_type operator()(Base<Args...>*);
constexpr std::false_type operator()(...);
};
}
template <template <typename...> typename Base, typename T>
struct is_template_base_of
: decltype(std::declval<detail::template_base_detector<Base>>()((T*)nullptr)) {};
// since C++ 14
template <template <typename...> typename Base, typename T>
constexpr bool is_template_base_of_v = is_template_base_of<Base, T>::value;
Depending on your c++ version, you can use different approaches to utilize this trait.
C++ 17
Probably the most compact solution. Since C++ 17 constexpr if statements are allowed, which allows us to define just a single func:
template <typename T>
void func(const T& t)
{
if constexpr (is_template_base_of_v<A, T>)
cout << 2 << endl;
else
cout << 1 << endl;
}
C++ 11 and 14
We have to fall back to tag dispatch:
namespace detail
{
template <typename T>
void func(std::true_type, const T& t)
{
std::cout << 2 << endl;
}
template <typename T>
void func(std::false_type, const T& t)
{
std::cout << 1 << endl;
}
}
template <typename T>
void func(const T& t)
{
detail::func(is_template_base_of<A, T>{}, t);
}
Tag dispatching.
First, we write a trait to detect if something has a template as a base:
namespace details {
template<template<class...>class Z>
struct htb {
template<class...Ts>
constexpr std::true_type operator()(Z<Ts...>*){return {};}
constexpr std::false_type operator()(...){return {};}
};
}
template<template<class...>class Z, class X>
constexpr inline auto has_template_base = details::htb<Z>{}((X*)nullptr);
we can now use our new trait to tag dispatch:
namespace details{
template <typename T>
void func(std::true_type,const A<T>& t) {
std::cout<<"2"<<std::endl;
}
template <class T>
void func(std::false_type,const T& t) {
std::cout<<"1"<<std::endl;
}
}
template <typename T>
void func(const T& t) {
details::func(has_template_base<A,T>,t);
}
Live example.
I want to have a generic function (or method) that accepts arguments of different types. If the provided type has 'one' method, the function should use it. If it has 'two' method, the function should use it instead.
Here's the invalid code:
#include <iostream>
template<typename Type> void func(Type t)
{
t.one();
}
template<typename Type> void func(Type t) // redefinition!
{
t.two();
}
class One
{
void one(void) const
{
std::cout << "one" << std::endl;
}
};
class Two
{
void two(void) const
{
std::cout << "two" << std::endl;
}
};
int main(int argc, char* argv[])
{
func(One()); // should print "one"
func(Two()); // should print "two"
return 0;
}
Is it possible to achieve using SFINAE? Is it possible to achieve using type_traits?
Clarification:
I would be more happy if this would be possible using SFINAE. The best-case scenario is: use first template, if it fails use the second one.
Checking for method existence is only an example. What I really want is also checking for compatibility with other classes.
The task could be rephrased:
If the class supports the first interface, use it.
If the first interface fails, use the second interface.
If both fail, report an error.
Yes, it's possible. In C++11 an onward it's even relatively easy.
#include <iostream>
#include <type_traits>
template<class, typename = void>
struct func_dispatch_tag :
std::integral_constant<int, 0> {};
template<class C>
struct func_dispatch_tag<C,
std::enable_if_t<std::is_same<decltype(&C::one), void (C::*)() const>::value>
> : std::integral_constant<int, 1> {};
template<class C>
struct func_dispatch_tag<C,
std::enable_if_t<std::is_same<decltype(&C::two), void (C::*)() const>::value>
> : std::integral_constant<int, 2> {};
template<class C>
void func(C const&, std::integral_constant<int, 0>) {
std::cout << "fallback!\n";
}
template<class C>
void func(C const &c, std::integral_constant<int, 1>) {
c.one();
}
template<class C>
void func(C const &c, std::integral_constant<int, 2>) {
c.two();
}
template<class C>
void func(C const &c) {
func(c, func_dispatch_tag<C>{});
}
struct One
{
void one(void) const
{
std::cout << "one\n";
}
};
struct Two
{
void two(void) const
{
std::cout << "two\n";
}
};
struct Three {};
int main(int argc, char* argv[])
{
func(One()); // should print "one"
func(Two()); // should print "two"
func(Three());
return 0;
}
Important points:
We SFINAE on the second parameter of func_dispatch_tag. The compiler looks at all the template specializations which result in the parameters <C, void>. Since any of the latter is "more specialized" when SF doesn't occur (i.e when std::enable_if_t is void), it gets chosen.
The chosen specialization of the trait defines a tag which we do a tag dispatch on. Tag dispatch depends on function overloading, instead of function template specialization (that cannot be partially specialized).
You can define a fallback function (like I did), or static_assert. The number of tags we can define is limited only by the range of an int, so extending to other members is just a matter of adding another func_dispatch_tag specialization.
The member must be accessible, or SF will occur. Also, a class that has both members will result in ambiguity. Bear that in mind.
Here's another way. There's a little more boilerplate, but in the actual expression of the different implementations of func() it could be argued that the 'list of tests that passed' is more expressive.
Food for thought anyway.
Code is c++11. c++14 and 17 would be more succinct.
#include <iostream>
#include <type_traits>
#include <tuple>
// boilerplate required prior to c++17
namespace notstd {
using namespace std;
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
}
// test for having member function one()
template<class T, class Enable = notstd::void_t<>> struct has_one : std::false_type {};
template<class T> struct has_one<T, notstd::void_t<decltype(std::declval<T>().one())>> : std::true_type {};
//test for having member function two()
template<class T, class Enable = notstd::void_t<>> struct has_two : std::false_type {};
template<class T> struct has_two<T, notstd::void_t<decltype(std::declval<T>().two())>> : std::true_type {};
// a type collection of tests that pass
template<template <class...> class...Tests> struct passes_tests {
};
// meta-function to append a type
template<class Existing, template <class...> class Additional> struct append_pass;
template< template <class...> class...Tests, template <class...> class Additional>
struct append_pass<passes_tests<Tests...>, Additional> {
using type = passes_tests<Tests..., Additional>;
};
//
// meta-functions to compute a list of types of test that pass
//
namespace detail
{
template<class Previous, class T, template<class...> class Test, template<class...> class...Rest>
struct which_tests_pass_impl
{
using on_pass = typename append_pass<Previous, Test>::type;
using on_fail = Previous;
using this_term = typename std::conditional< Test<T>::value, on_pass, on_fail >::type;
using type = typename which_tests_pass_impl<this_term, T, Rest...>::type;
};
template<class Previous, class T, template<class...> class Test>
struct which_tests_pass_impl<Previous, T, Test>
{
using on_pass = typename append_pass<Previous, Test>::type;
using on_fail = Previous;
using this_term = typename std::conditional< Test<T>::value, on_pass, on_fail >::type;
using type = this_term;
};
}
template<class Type, template<class...> class...Tests> struct which_tests_pass
{
using type = typename detail::which_tests_pass_impl<passes_tests<>, Type, Tests...>::type;
};
//
// various implementations of func()
//
namespace detail
{
template<class T>
void func(T t, passes_tests<has_one>)
{
t.one();
}
template<class T>
void func(T t, passes_tests<has_one, has_two>)
{
t.one();
}
template<class T>
void func(T t, passes_tests<has_two>)
{
t.two();
}
template<class T>
void func(T t, passes_tests<>)
{
// do nothing
}
}
template<class T>
void func(T t)
{
detail::func(t, typename which_tests_pass<T, has_one, has_two>::type());
}
//
// some types
//
struct One
{
void one(void) const
{
std::cout << "one" << std::endl;
}
};
struct Two
{
void two(void) const
{
std::cout << "two" << std::endl;
}
};
// test
int main(int argc, char* argv[])
{
func(One()); // should print "one"
func(Two()); // should print "two"
return 0;
}
The code below
handles member function constness correctly
is agnostic of the functions return types
prints a comprehensive error on failure
It could be even shorter with C++14, where you don't have to specify the return types of implemented functions and have templated variable declarations. If you want to handle rvalue overloads correctly, you need to provide another overload to as_memfun.
If testing for member functions alone is not enough, there is another approach in the last section, which offers far better customization options but is also lengthier to setup.
#include <utility>
#include <functional>
namespace detail {
template<typename T> struct _false : std::integral_constant<bool, false> { };
template<typename T> struct HasNone {
static_assert(_false<T>::value, "No valid method found");
};
template<typename T, typename R>
constexpr auto as_memfun (R (T::* arg) ())
-> R (T::*) ()
{ return arg; }
template<typename T, typename R>
constexpr auto as_memfun (R (T::* arg) () const)
-> R (T::*) () const
{ return arg; }
template<typename T> constexpr auto check_has_two(int)
-> decltype(as_memfun(&T::two))
{ return as_memfun(&T::two); }
template<typename T> constexpr auto check_has_two(...)
-> HasNone<T>;
template<typename T> constexpr auto check_has_one(int)
-> decltype(as_memfun(&T::one))
{ return as_memfun(&T::one); }
template<typename T> constexpr auto check_has_one(...)
-> decltype(check_has_two<T>(0))
{ return check_has_two<T>(0); }
template<typename T>
struct res { constexpr static auto detail = check_has_one<T>(0); };
}
template<typename T>
auto func(T t) -> decltype((t.*detail::res<T>::detail)()) {
return (t.*detail::res<T>::detail)();
}
And here are some test you would probably like to have
struct One {
void one();
};
struct Two {
void two();
};
struct TestBoth {
char one() const;
void two();
};
struct TestWilderStuff {
int one;
void two() const;
};
int main() {
func(One{});
func(Two{});
func(TestBoth{});
static_assert(decltype(func(TestBoth{})){} == 0, "Failed function selection");
func(TestWilderStuff{});
}
Since you seem to have more extensive constructions in mind than just testing for member function existence, here is the beginning of a vastly more powerful mechanism. You can use it as a drop-in replacement for the above solution and although it is far lengthier, it offers more customization and the possibility to do elaborate tests on your types in every step of the way.
#include <utility>
#include <functional>
namespace detail {
template<typename T> struct _false :
std::integral_constant<bool, false> { };
template<typename T> struct HasNone {
static_assert(_false<T>::value, "No valid method found");
};
// Generic meta templates used below
namespace Generics {
template<typename Getter, typename Else>
struct ChainGetter {
template<typename T> constexpr static auto get_(int)
-> decltype(Getter::template get<T>())
{ return Getter::template get<T>(); }
template<typename T> constexpr static auto get_(...)
-> decltype(Else::template get<T>())
{ return Else::template get<T>(); }
template<typename T> constexpr static auto get()
-> decltype(get_<T>(0))
{ return get_<T>(0); }
};
template<typename Getter, typename Test>
struct TestGetter {
template<typename T, typename R> using _type = R;
template<typename T> constexpr static auto get_()
-> decltype(Getter::template get<T>())
{ return Getter::template get<T>(); }
template<typename T> constexpr static auto test()
-> decltype(Test::template test<T>(get_<T>()));
template<typename T> constexpr static auto get()
-> _type<decltype(test<T>()),
decltype(get_<T>())
>
{ return get_<T>(); }
};
template<template<typename> class F>
struct FailGetter {
template<typename T>
constexpr static auto get() -> F<T>;
};
}
// Test only exists for member function pointer arguments
struct IsMemberFunctionTest {
template<typename _, typename T, typename R>
constexpr static void test (R (T::* arg) ());
template<typename _, typename T, typename R>
constexpr static void test (R (T::* arg) () const);
};
// Get member pointer to T::one
struct GetOne {
template<typename T>
constexpr static auto get() -> decltype(&T::one) { return &T::one; }
};
// Get member pointer to T::two
struct GetTwo {
template<typename T>
constexpr static auto get() -> decltype(&T::two) { return &T::two; }
};
using namespace Generics;
using getter_fail = FailGetter<HasNone>;
using get_two_tested = TestGetter<GetTwo, IsMemberFunctionTest>;
using getter_two = ChainGetter<get_two_tested, getter_fail>;
using get_one_tested = TestGetter<GetOne, IsMemberFunctionTest>;
using getter_one = ChainGetter<get_one_tested, getter_two>;
template<typename T>
struct result { constexpr static auto value = getter_one::template get<T>(); };
}
template<typename T>
auto func(T t) -> decltype((t.*detail::result<T>::value)()) {
return (t.*detail::result<T>::value)();
}
I can imagine the following code:
template <typename T> class X
{
public:
T container;
void foo()
{
if(is_vector(T))
container.push_back(Z);
else
container.insert(Z);
}
}
// somewhere else...
X<std::vector<sth>> abc;
abc.foo();
How to write it, to successfully compile? I know type traits, but when I'm defining:
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 {};
It doesn't compile:
error: no matching function for call to 'std::vector<sth>::insert(Z)'
static_assert also isn't that what I'm looking for. Any advices?
Here's a short example of what I want to achieve (SSCCE): http://ideone.com/D3vBph
It is named tag dispatching :
#include <vector>
#include <set>
#include <type_traits>
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 X {
T container;
void foo( std::true_type ) {
container.push_back(0);
}
void foo( std::false_type ) {
container.insert(0);
}
public:
void foo() {
foo( is_vector<T>{} );
}
};
// somewhere else...
int main() {
X<std::vector<int>> abc;
abc.foo();
X<std::set<int>> def;
def.foo();
}
An alternative worth considering is to detect the presence of the push_back function using SFINAE. This is slightly more generic since it'll translate to other containers that implement push_back.
template<typename T>
struct has_push_back
{
template<typename U>
static std::true_type test(
decltype((void(U::*)(const typename U::value_type&)) &U::push_back)*);
template<typename>
static std::false_type test(...);
typedef decltype(test<T>(0)) type;
static constexpr bool value =
std::is_same<type, std::true_type>::value;
};
Note that it currently only detects push_back(const T&) and not push_back(T&&). Detecting both is a little more complicated.
Here's how you make use of it to actually do the insert.
template<typename C, typename T>
void push_back_impl(C& cont, const T& value, std::true_type) {
cont.push_back(value);
}
template<typename C, typename T>
void push_back_impl(C& cont, const T& value, std::false_type) {
cont.insert(value);
}
template<typename C, typename T>
void push_back(C& cont, const T& value) {
push_back_impl(cont, value, has_push_back<C>::type());
}
std::vector<int> v;
push_back(v, 1);
std::set<int> s;
push_back(s, 1);
Honestly, this solution became a lot more complicated then I originally anticipated so I wouldn't use this unless you really need it. While it's not too hard to support const T& and T&&, it's even more arcane code that you have to maintain which is probably not worth it in most cases.
Using insert only:
#include <iostream>
#include <vector>
#include <set>
template <typename T>
class X
{
public:
T container;
template <typename U>
void insert(const U& u) {
container.insert(container.end(), u);
}
};
int main() {
X<std::vector<int>> v;
v.insert(2);
v.insert(1);
v.insert(0);
for(std::vector<int>::const_iterator pos = v.container.begin();
pos != v.container.end();
++pos)
{
std::cout << *pos;
}
std::cout << '\n';
X<std::set<int>> s;
s.insert(2);
s.insert(1);
s.insert(0);
for(std::set<int>::const_iterator pos = s.container.begin();
pos != s.container.end();
++pos)
{
std::cout << *pos;
}
std::cout << '\n';
}
Here's the typical method using void_t:
template <typename T>
using void_t = void; // C++17 std::void_t
template <typename C, typename = void> // I'm using C for "container" instead of T, but whatever.
struct has_push_back_impl : std::false_type {};
template <typename C>
struct has_push_back_impl<C, void_t<decltype(std::declval<C>().push_back(typename C::value_type{}))>>
: std::true_type {}; // Note that void_t is technically not needed in this case, since the 'push_back' member function actually returns void anyway, but it the general method to pass the type into void_t's template argument to obtain void. For example, the 'insert' function from std::set and std::map do NOT return void, so 'has_insert' will need to use void_t.
template <typename C>
using has_push_back = has_push_back_impl<C>; // void passed to the second template argument by default, thus allowing the second specialization to be used instead of the primary template whenever C has a push_back member function.
This method will work for has_insert for associative containers, even though std::set, std::map's insert function return std::pair<typename T::iterator, bool> while std::multimap::insert returns std::multimap::iterator (this is one case where Ze Blob's method will not work).
If you use constexpr if, you were doing it right. This C++17 code compiles:
#include <iostream>
#include <type_traits>
#include <vector>
#include <list>
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 X
{
public:
T container;
void foo()
{
if constexpr(is_vector<T>::value){
std::cout << "I am manipulating a vector" << std::endl;
// Can access container.push_back here without compilation error
}
else {
std::cout << "I am manipulating something else" << std::endl;
}
}
};
int main() {
X<std::vector<int>> abc;
abc.foo(); // outputs "I am manipulating a vector"
X<std::list<int>> def;
def.foo(); // outputs "I am manipulating something else"
}
in C++20 using requires expression:
#include <type_traits>
#include <concepts>
#include <vector>
template<class T>
static constexpr bool is_vector_v = requires {
requires std::same_as<std::decay_t<T>,
std::vector<typename std::decay_t<T>::value_type> >;
};
and in code:
template<class T>
void foo() {
if constexpr (is_vector_v<T>)
container.push_back(Z);
else
container.insert(Z);
}