Okay, simple template question. Say I define my template class something like this:
template
class foo {
public:
foo(T const& first
Here's the latest and expanded version of this answer and significant improvement over answer by Sabastian.
The idea is to define all traits of STL containers. Unfortunately, this gets tricky very fast and fortunately lot of people have worked on tuning this code. These traits are reusable so just copy and past below code in file called type_utils.hpp (feel free to change these names):
//put this in type_utils.hpp
#ifndef commn_utils_type_utils_hpp
#define commn_utils_type_utils_hpp
#include
#include
namespace common_utils { namespace type_utils {
//from: https://raw.githubusercontent.com/louisdx/cxx-prettyprint/master/prettyprint.hpp
//also see https://gist.github.com/louisdx/1076849
namespace detail
{
// SFINAE type trait to detect whether T::const_iterator exists.
struct sfinae_base
{
using yes = char;
using no = yes[2];
};
template
struct has_const_iterator : private sfinae_base
{
private:
template static yes & test(typename C::const_iterator*);
template static no & test(...);
public:
static const bool value = sizeof(test(nullptr)) == sizeof(yes);
using type = T;
void dummy(); //for GCC to supress -Wctor-dtor-privacy
};
template
struct has_begin_end : private sfinae_base
{
private:
template
static yes & f(typename std::enable_if<
std::is_same(&C::begin)),
typename C::const_iterator(C::*)() const>::value>::type *);
template static no & f(...);
template
static yes & g(typename std::enable_if<
std::is_same(&C::end)),
typename C::const_iterator(C::*)() const>::value, void>::type*);
template static no & g(...);
public:
static bool const beg_value = sizeof(f(nullptr)) == sizeof(yes);
static bool const end_value = sizeof(g(nullptr)) == sizeof(yes);
void dummy(); //for GCC to supress -Wctor-dtor-privacy
};
} // namespace detail
// Basic is_container template; specialize to derive from std::true_type for all desired container types
template
struct is_container : public std::integral_constant::value &&
detail::has_begin_end::beg_value &&
detail::has_begin_end::end_value> { };
template
struct is_container : std::true_type { };
template
struct is_container : std::false_type { };
template
struct is_container> : std::true_type { };
template
struct is_container> : std::true_type { };
template
struct is_container> : std::true_type { };
}} //namespace
#endif
Now you can use these traits to make sure our code only accepts container types. For example, you can implement append function that appends one vector to another like this:
#include "type_utils.hpp"
template
static typename std::enable_if::value, void>::type
append(Container& to, const Container& from)
{
using std::begin;
using std::end;
to.insert(end(to), begin(from), end(from));
}
Notice that I'm using begin() and end() from std namespace just to be sure we have iterator behavior. For more explanation see my blog post.