partial template specialisation on template class and member function - c++

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;
}

Related

Why does this c++ template scenario unable to compile:

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>();
}

How to avoid writing std::reference_wrapper version template all the time?

I have a function template to echo something from the class, for example:
template<typename T>
void say(const std::vector<T>& ts)
{
for (const auto& t : ts) {
std::cout<<t.getDesc()<<std::endl;
}
}
class Base
{
public
Base();
const std::string& getDesc() const {
return m_desc;
}
protected:
std::string m_desc;
}
All objects inherited from Base can use the template function.
std::vector<Base> v;
Base a;
v.push_back(a)
But when I turn to std::reference_wrapper, the function template does not work. I should write another template to fix it.
template<typename T>
void say(const std::vector<std::reference_wrapper<T>>& ts)
{
for (const auto& t : ts) {
std::cout<<t.get().getDesc()<<std::endl;
}
}
The only difference between them is the std::reference_wrapper::get().
So is there any other way to avoid this annoying code? Or should I write std::reference_wrapper version template for all the function I used?
You don't need to rewrite the whole thing. Just use a level of indirection.
namespace detail {
template<typename T>
constexpr T& get(T &t) { return t; }
template<typename T>
constexpr T const& get(T const &t) { return t; }
template<typename T>
constexpr T& get(std::reference_wrapper<T> rt) { return rt; }
}
template<typename T>
void say(const std::vector<T>& ts)
{
for (const auto& t : ts) {
std::cout<< detail::get(t) <<std::endl;
}
}
The above functions in the detail namespace are reusable of course (though could be better named), and can make all your templates work with std::reference_wrapper.
You can use a helper Unwrap template such as boost::unwrap_reference. Or write your own, copying the code from this other answer:
template< typename T >
struct UnwrapReference;
template< typename T >
struct UnwrapReference { typedef T type; }
template< >
struct UnwrapReference< std::reference_wrapper< T > > { typedef T type; }
Now you can write one version of your function to bind them all (untested):
template<typename T>
void say(const std::vector<T>& ts)
{
for (const UnwrapReference<T>::type &t : ts) {
std::cout<<t.getDesc()<<std::endl;
}
}

Using std::enable_if

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).

Apply a tuple of functions to a value and return a tuple

Right now, I have the following to apply two functions to a value and return a 2-value tuple:
template<typename F1, typename F2>
class Apply2
{
public:
using return_type = std::tuple<typename F1::return_type, typename F2::return_type>;
Apply2(const F1& f1, const F2& f2) : f1_(f1), f2_(f2) {}
template<typename T> return_type operator()(const T& t) const
{
return std::make_tuple(f1_(t), f2_(t));
}
protected:
const F1& f1_;
const F2& f2_;
};
I wanted to generalize this to N functions:
template<typename ...F>
class ApplyN
{
public:
using return_type = std::tuple<typename F::return_type...>;
ApplyN(const std::tuple<F...>& fs) : functions_(fs) {}
template<typename T> return_type operator()(const T& t) const
{
return ???;
}
protected:
std::tuple<F...> functions_;
};
I know I probably need to use template recursion somehow, but I can't wrap my head around it. Any ideas?
It took me a while, but here it is (using indices):
template<typename ...F>
class ApplyN
{
public:
using return_type = std::tuple<typename F::return_type...>;
ApplyN(const F&... fs) : functions_{fs...} {}
template<typename T> return_type operator()(const T& t) const
{
return with_indices(t, IndicesFor<std::tuple<F...> >{});
}
protected:
std::tuple<F...> functions_;
template <typename T, std::size_t... Indices>
return_type with_indices(const T& t, indices<Indices...>) const
{
return return_type{std::get<Indices>(functions_)(t)...};
}
};
Someone had an (incomplete) answer before, but s/he erased it - that was my starting point. Anyway, thank you stranger! Thank you R. Martinho Fernandes too!

How to define a template function for a reference parameter and the same function for a pointer parameter

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.