I have a class
template <typename T, typename W>
class A {
void foo(W);
void foo(T);
void foo(int);
}
When T=int, W=int, or W=T, this class fails to compile. How can I get the methods to take priority over each other?
I want the priority W > T > int. So if W=T, foo(T) is ignored and foo(W) is called. If T=int, foo(int) is ignored and foo(T) is called.
The compiler is VS2012, but I have Linux too, and will consider GCC/Clang solutions as well. Anything that compiles on any mainstream compiler goes, but only if you say what compilers it works on.
I would tag dispatch. Override dispatching is easy to understand and scales.
We start with a perfect forwarder:
template<class U> void foo(U&&u){
foo( std::forward<U>(u), std::is_convertible<U, W>{}, std::is_convertible<U,T>{} );
}
it creates tag types, in this case true or false types, to dispatch on.
This one:
void foo( W, std::true_type, ... );
catches everything that can convert to W.
Next, we block this one:
void foo( T, std::false_type, std::true_type );
from considerimg cases where the first argument can convert to W.
Finally, this one:
void foo( int, std::false_type, std::false_type );
can only be considered if the first parameter cannot convert to either.
Fancier tag types, or doing the dispatching one at a time, are both possible.
Sorry for typos.
I use a single C++11 feature -- {} to construct an object -- above. If your compiler lacks support for that C++11 feature, simply upgrade your compiler, it is 2014, get with it. Failing that, replace {} with ().
Use std::enable_if:
#include <type_traits>
template <typename T, typename W>
struct A {
void foo(W) {}
template<typename XT=T> typename std::enable_if<std::is_same<XT,T>::value
&& !std::is_same<T, W>::value, void>::type foo(T) {}
template<typename XT=int> typename std::enable_if<std::is_same<XT,int>::value
&& !std::is_same<int, T>::value
&& !std::is_same<int, W>::value, void>::type foo(int) {}
};
Added for testing:
template struct A<short,char>;
template struct A<char,char>;
template struct A<char,int>;
template struct A<int,char>;
template struct A<int, int>;
struct S {};
int main() {
A<S, int>{}.foo(S{});
}
For the relevant part of your template, you could use speclializations:
template <typename U, typename W>
struct Foo
{
void f(U);
void f(W);
};
template <typename T>
struct Foo<T, T>
{
void f(T);
};
For the rest of your class or class template, you can inherit from Foo<A, B> so you can keep the common code out of the part that needs to be specialized:
template <typename A, typename B>
struct TheClass : Foo<A, B>
{
// common code
};
Try template specializations:
template <typename T, typename W>
class A {
void foo(W);
void foo(T);
void foo(int);
};
template <typename T>
class A<T, T> {
void foo(T);
void foo(int);
};
template <>
class A<int, int> {
void foo(int);
};
Here is a solution without specializations of A, but with two helper structures in a few forms.
#include <iostream>
template<typename T, typename W>
struct T_type { typedef T type; };
template<typename W>
struct T_type<W, W> { typedef void* type; /*dummy type*/};
template<typename T, typename W>
struct int_type { typedef int type; };
template<typename W>
struct int_type<int, W> { typedef void** type; /*dummy type*/};
template<typename T>
struct int_type<T, int> { typedef void** type; /*dummy type*/};
template<>
struct int_type<int, int> { typedef void** type; /*dummy type*/};
template<typename T, typename W>
class A {
public:
void foo(W w) {
std::cout << "foo(W)" << std::endl;
}
void foo(typename T_type<T, W>::type t) {
std::cout << "foo(T)" << std::endl;
}
void foo(typename int_type<T, W>::type i) {
std::cout << "foo(int)" << std::endl;
}
};
int main() {
std::cout << "A<float, char>" << std::endl;
A<float, char> a;
a.foo(1.0f);
a.foo('1');
a.foo(1);
std::cout << "A<float, float>" << std::endl;
A<float, float> b;
b.foo(1.0f);
b.foo(1);
std::cout << "A<int, int>" << std::endl;
A<int, int> c;
c.foo(1);
return 0;
}
Related
This question is awful similar to How to tell if template type is an instance of a template class?
I would like to detect if a template parameter is from one particular template class that has no variadic template arguments.
template<class U, class S>
struct A{};
template<class T>
struct B {
B() {
if constexpr (T == A) {
// T is a template instantiation of `A`.
} else {
}
}
};
I can't change A's definition. I can change B's definition to have additional template parameters.
How do I implement (T == A) given the restriction of not knowing A's U and S?
I would go for a partial specialization here.
#include <iostream>
template<class U, class S>
struct A{};
template<class T>
struct B {
B() {
std::cout << "None-A implementation\n";
}
};
template<class U, class S>
struct B<A<U, S>> {
B() {
std::cout << "A implementation\n";
}
};
int main() {
B<int> b1;
B<A<int, int>> b2;
}
You have the option of leaving the default-case without an implementation.
Or you can have a fallback implementation for any none-A classes like here.
If the partial specialization forces too much code duplication you can also extract the detection part to it's own template variable like this.
#include <iostream>
template<class U, class S>
struct A{};
template <class T>
constexpr bool is_A_instatiation = false;
template <class U, class S>
constexpr bool is_A_instatiation<A<U, S>> = true;
template<class T>
struct B {
B() {
if constexpr (is_A_instatiation<T>) {
std::cout << "A instatiation!\n";
} else {
std::cout << "none-A instatiation!\n";
}
}
};
int main() {
B<int> b1;
B<A<int, int>> b2;
}
The easiest way is:
template<class T>
struct B{/*default implementation*/};
template<class U,class S>
struct B<A<U,S>>{/*Specified implementation*/};
A<T,U>: you already know it and search key
B<...>: variadic types which may include A<T,U> - known type
And you want to search A<T,U> in B<...>
template <typename T, typename U>
struct A {};
template <typename T, typename U, typename ...Ts>
struct B {
static constexpr bool value = ((std::is_same_v< A<T, U>, Ts> || ... ));
};
int main() {
std::cout << std::boolalpha <<
B<int,float, int, int, float, A<int,float>>::value << '\n'<<
B<int,float, int, int, float>::value <<std::endl;
}
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.
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
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);
}
Consider the following case:
void Set(const std::function<void(int)> &fn);
void Set(const std::function<void(int, int)> &fn);
Now calling the function with
Set([](int a) {
//...
});
Gives "ambiguous call to overloaded function" error. I am using Visual Studio 2010. Is there a work around or another method to achieve something similar. I cannot use templates, because these functions are stored for later use because I cannot determine the number of parameters in that case. If you ask I can submit more details.
I would suggest this solution. It should work with lambdas as well as with function-objects. It can be extended to make it work for function pointer as well (just go through the link provided at the bottom)
Framework:
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
{
enum { arity = sizeof...(Args) };
};
template<typename Functor, size_t NArgs>
struct count_arg : std::enable_if<function_traits<Functor>::arity==NArgs, int>
{};
Usage:
template<typename Functor>
typename count_arg<Functor, 1>::type Set(Functor f)
{
std::function<void(int)> fn = f;
std::cout << "f with one argument" << std::endl;
}
template<typename Functor>
typename count_arg<Functor, 2>::type Set(Functor f)
{
std::function<void(int, int)> fn = f;
std::cout << "f with two arguments" << std::endl;
}
int main() {
Set([](int a){});
Set([](int a, int b){});
return 0;
}
Output:
f with one argument
f with two arguments
I took some help from the accepted answer of this topic:
Is it possible to figure out the parameter type and return type of a lambda?
Work around for Visual Studio 2010
Since Microsoft Visual Studio 2010 doesn't support variadic templates, then the framework-part can be implemented as:
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{};
template <typename C, typename R, typename T0>
struct function_traits<R(C::*)(T0) const> { enum { arity = 1 }; };
template <typename C, typename R, typename T0, typename T1>
struct function_traits<R(C::*)(T0,T1) const> { enum { arity = 2 }; };
template <typename C, typename R, typename T0, typename T1, typename T2>
struct function_traits<R(C::*)(T0,T1,T2) const> { enum { arity = 3 }; };
//this is same as before
template<typename Functor, size_t NArgs, typename ReturnType=void>
struct count_arg : std::enable_if<function_traits<Functor>::arity==NArgs, ReturnType>
{};
EDIT
Now this code supports any return type.
I suggest:
void Set(void(*f)(int, int))
{
std::function<void(int,int)> wrap(f);
// ...
}
void Set(void(*f)(int))
{
std::function<void(int)> wrap(f);
// ...
}
You can manually specify the type:
Set(std::function<void(int)>([](int a) {
//...
}));