I'm trying to specialize a function for a range of types using std::enable_if.
Here is a a simpler version of what I'm trying to accomplish.
#include <type_traits>
#include <string>
struct Ip
{};
template <typename T>
bool Equal(const std::string& s, const T& data)
{return s == data;}
template <typename T>
bool Equal(const std::string& s, const typename std::enable_if<std::is_integral<T>::value, T>::type& data)
{
//int specific
}
template <typename T>
bool Equal(const std::string& s, const typename std::enable_if<std::is_floating_point<T>::value, T>::type& data)
{
//Float specific
}
//Specialization
template <> bool Equal(const std::string& s, const Ip& data)
{
//Ip specific
}
int main()
{
//Equal("21",21); // Do not compile
Equal("21","42.5"); // Compile
}
but when trying to compile, the template functions with std::enable_if does not seem to participate in the resolution, and so the compiler tells me that there is no function that match my function call. I tried using std::enable_if with the return type, but no luck there either. I'm sure there is something I'm doing terribly wrong in this code, or maybe I'm trying to do this the wrong way. I'm trying to not write every int specialization (int, short, long, long long, ...), so does anyone have a solution for this ?
I'd make several changes to your code, beginning with the unconstrained function template. Since you're handling arithmetic types separately, that should only be selected if T is neither integral nor floating point.
template <typename T>
typename std::enable_if<!std::is_integral<T>::value &&
!std::is_floating_point<T>::value, bool>::type
Equal(const std::string& s, const T& data)
{return s == data;}
Now, the way you've defined Equal for integral and floating point types causes the T to be a non-deduced context. So move the enable_if to a dummy template parameter to allow deduction (you could also use the return type, as above).
template <typename T,
typename std::enable_if<std::is_integral<T>::value, T>::type* = nullptr>
bool Equal(const std::string& s, const T& data)
{
//int specific
return false;
}
template <typename T,
typename std::enable_if<std::is_floating_point<T>::value, T>::type* = nullptr>
bool Equal(const std::string& s, const T& data)
{
//Float specific
return false;
}
Finally, there's no need to specialize for handling Ip, create an overload instead.
//Overload
bool Equal(const std::string& s, const Ip& data)
{
//Ip specific
return false;
}
Live demo
Following works: (https://ideone.com/G1vJKm)
#include <string>
#include <type_traits>
struct Ip
{
std::string ip;
};
template <typename T>
typename std::enable_if<!std::is_integral<T>::value && !std::is_floating_point<T>::value, bool>::type
Equal(const std::string& s, const T& data)
{return s == data;}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
Equal(const std::string& s, const T& data)
{
//int specific
return false;
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
Equal(const std::string& s, const T& data)
{
//Float specific
return false;
}
bool Equal(const std::string& s, const Ip& data)
{
//Ip specific
return false;
}
In your case,
template <typename T>
bool Equal(const std::string& s, const T& data)
is a valid candidate for all T, also for integral and floating point.
Your other template functions, compiler can't deduce type T for typename std::enable_if<std::is_integral<T>::value, T>::type.
And for the Ip version a simpler overload is sufficient (and preferred).
Related
struct stream_type1 {
template<typename T>
const T& read() const;
};
struct stream_type2 {
template<typename T>
const T& read() const;
};
template<typename S, typename T>
const T& stream_read(const S& stream)
{
return stream.read<T>();
}
// example:
stream_type1 stream1;
stream_type1 stream2;
int value1 = stream_read<int>(stream1);
int value2 = stream_read<int>(stream2);
error: C2665: 'stream_read': none of the 2 overloads could convert all the argument types
so, I have to specialize the template witch makes it redundant
template<typename T>
const T& stream_read(const stream_type1 & stream)
{
return stream.read<T>();
}
template<typename T>
const T& stream_read(const stream_type2 & stream)
{
return stream.read<T>();
}
You have your template parameters the wrong way round to deduce the stream type. At the moment you have instantiated it as
template<typename T>
const T& stream_read(const int& stream)
{
return stream.read<T>();
}
You can swap T and S
template<typename T, typename S>
const T& stream_read(const S& stream)
{
return stream.read<T>();
}
I have a class with several strongly typed enums and I want them to be implicitly comparable to int. For that purpose I have written super-simple template function:
template<class T> bool operator==(const T &e, const int &i)
{
return (static_cast<int>(e) == i);
}
and the opposite one
template<class T> bool operator==(const int &i, const T &e)
{
return (e == i);
}
However this might be dangerous because it pretty much writes a blank check for any comparison between class T and an integer. I would therefore want only few explicitly specified instantiations such as
template bool operator==(const MyEnum &e, const int &i);
template bool operator==(const int &i, const MyEnum &e);
and no other. Is there a way to disable all implicit instantiations or achieve this? I can always write both operators for each enum separately of course but templates really seem like a better solution.
Use SFINAE to reject overloads which don't deduce a MyEnum:
template<class T, std::enable_if_t<std::is_same<T, MyEnum>::value>* = nullptr>
bool operator==(const T &e, const int &i);
template<class T, std::enable_if_t<std::is_same<T, MyEnum>::value>* = nullptr>
bool operator==(const int &i, const T &e);
Due to the comments I have updated my answer. If you want to only accept select few template types then I suggest using this bool_or trick:
#include <type_traits>
#include <utility>
template<bool... Bs>
using bool_sequence = std::integer_sequence<bool, Bs...>;
template<bool... Bs>
using bool_and = std::is_same<bool_sequence<Bs...>,
bool_sequence<(Bs || true)...>>;
template<bool... Bs>
using bool_or = std::integral_constant<bool, !bool_and<!Bs...>::value>;
template<class T, class... Ts>
using any_same = bool_or<std::is_same<T, Ts>::value...>;
template<class T, std::enable_if_t<any_same<T, MyEnum, X, Y, Z>::value>* = nullptr>
bool operator==(const T &e, const int &i);
template<class T, std::enable_if_t<any_same<T, MyEnum, X, Y, Z>::value>* = nullptr>
bool operator==(const int &i, const T &e);
You can add any number of types to compare with.
I have a mechanism that uses function overloads to decompose a type into primitives. Here is a sample of the mechanism:
template <typename M, typename T> void process(M&, T&);
template <typename M, typename T>
void process_impl(M& m, const T& t) {
m.write(t);
}
template <typename M, typename T>
void process_impl(M& m, const std::vector<T>& v) {
for (const auto& t: v) {
process(m, t);
}
}
template <typename M, typename T>
void process(M& m, const T& t) {
process_impl(m, t);
}
The use case is to feed a type to process(), which will decompose it into simple types using the overloads of process_impl(). In order to customize the behavior of process() for a specific type, simply overload process():
struct media {
template <typename T> void write(const T& t) { std::cout << t << std::endl; }
};
void process(media& m, const std::string& s) {
process(m, s.size());
}
This code:
media m;
process(m, std::vector<std::string>{"1", "22"});
Outputs:
1
2
Now, I want to wrap media in a class, such that I can further tweak the behavior of process() for my wrapper structure, but I want to preserve the initial behavior of process() regarding media:
template <typename M>
struct debug_media {
template <typename T> void write(const T& t) { m.write(t); }
M m;
};
template <typename M, typename T>
void process(debug_media<M>& d, const T& t) {
std::cout << "debug: ";
process(d.m, t);
}
Ideally, I want the output to be:
debug: 1
debug: 2
But that is not the case, since the overload for debug_media is used only once, for the first invocation; afterward, the overloads of process() that accept a media are used, not the one with debug_media<media>.
I cannot have inheritance between media and debug_media. Is it possible to preserve the behavior of both overloads?
I have put sample code on ideone.
You can add the extra overload:
template <typename M, typename T> void process(debug_media<M>& d, const std::vector<T>& v)
{
for(const auto& t : v) {
process(d, t);
}
}
Live example
It's been an hour since I am doing some research for informations about partial template specialisation.
Unfortunately this is not successful ..
I still found a lot of information, but not to solve my problem.
So I hope that someone can help me.
Consider the following minimal code:
SQLObject.hpp
template<typename T>
class SQLObject
{
public:
template<typename U>
static std::list<T*> filter(const std::string& colum,const std::string& ope,const U& value);
static std::list<T*> filter(const Filter& filter);
}
#include "SQLObject.tpl"
SQLObject.tpl
#include "Filter.hpp"
/* This code do not work, but why ??? */
template<typename T>
template<>
std::list<T*> SQLObject<T>::filter<std::string>(const std::string& colum,const std::string& ope,const std::string& value)
{
// no to_string need whith std::string
return filter(Filter(colum,ope,value));
}
template<typename T>
template<typename U>
std::list<T*> SQLObject<T>::filter(const std::string& colum,const std::string& ope,const U& value)
{
//use to_string with all others types
return filter(Filter(colum,ope,std::to_string(value)));
}
template<typename T>
std::list<T*> SQLObject<T>::filter(const Filter& filter)
{
//some stuff
}
My problem is the following:
I am not able to specialize filter with std :: string.
So I tried a simple overload, but without success.
So I turn to you, hoping that you could help me.
Short answer: You can't explicitly specialize a member template of an class template that is not explicitly specialized.
I guess using an overload as you suggested might be the simplest solution:
#include <list>
#include <string>
struct Filter
{
// you constructor...
template < typename... T > Filter(T...){}
};
template<typename T>
class SQLObject
{
public:
template<typename U>
static std::list<T*> filter(const std::string& colum,
const std::string& ope,const U& value);
// v-here-v is the overload
static std::list<T*> filter(const std::string& colum,
const std::string& ope,
const std::string& value);
static std::list<T*> filter(const Filter& filter);
};
// works
template<typename T>
std::list<T*> SQLObject<T>::filter(const std::string& colum,
const std::string& ope,
const std::string& value)
{
// no to_string need whith std::string
return filter(Filter(colum,ope,value));
}
//[...]
But in this particular case, there's even a simpler solution than that:
std::string const& to_string(std::string const& p) { return p; }
// class definition etc.
template<typename T>
template<typename U>
std::list<T*> SQLObject<T>::filter(const std::string& colum,
const std::string& ope,const U& value)
{
//use to_string with all others types
using std::to_string;
return filter(Filter(colum,ope,to_string(value)));
}
Similar to this question (or this question, as pointed out by DyP above). Here is a compiling specialization with the class specialized to int.
template<typename T>
class SQLObject
{
public:
template<typename U>
static std::list<T*> filter(const std::string& colum,const std::string& ope,const U& value);
};
/* This code do not work, but why ??? */
template<>
template<>
std::list<int *> SQLObject<int>::filter<typename std::string>(const std::string& colum,const std::string& ope,const std::string& value)
{
// no to_string need whith std::string
return list<int *>();
}
template<typename T>
template<typename U>
std::list<T*> SQLObject<T>::filter(const std::string& colum,const std::string& ope,const U& value)
{
//use to_string with all others types
return filter(Filter(colum,ope,std::to_string(value)));
}
int main() {
return 0;
}
I want to define a templated functor for name comparison, that takes references as well
as pointers. I want to use this for a normal find_if on a container of elements as well as for a container of pointers (unfortunately ptr_vector or the like is not an option).
The best solution I have found so far is the following.
template <typename U>
class by_name{
public:
by_name(U const& pName):mName(pName) {}
template <class T>
typename boost::disable_if_c<boost::is_pointer<T>::value, bool>::type
operator()(T const& pX){ return pX.getName()== mName;}
template <class T>
typename boost::enable_if_c<boost::is_pointer<T>::value, bool>::type
operator()(T pX){ return pX->getName()== mName;}
private:
U mName;
};
This looks quite ugly and very hard to understand for people not knowing enable_if.
Is there an easier way to write such a functor taking pointer and reference alike?
It can be as simple as:
template <class T>
bool operator()(T const& rX) const { return rX.getName() == mName; }
template <class T>
bool operator()(T* const pX) const { return pX->getName() == mName; }
Do the classes that implement getName member functions return anything else than std::string? If not, you can get rid of one template parameter.
This is how I would have implemented the functor:
class by_name
{
public:
by_name(const std::string& name) :
Name(name) {}
template <class T>
bool operator()(T const& pX) const
{
return pX.getName() == Name;
}
template <class T>
bool operator()(T* pX) const
{
if (!pX) // how do you handle a null ptr?
return false;
(*this)(*pX); // #Luc Danton
}
private:
std::string Name;
};
If the pointer version is implemented as
bool operator(T const* pX) const {}
gcc for some reason choose to instantiate
bool operator(T const& pX) const with [T = A*]
The functor has been compiled and tested with gcc 4.6.1.