I am currently doing some template metaprogramming. In my case I can handle any "iteratable" type, i.e. any type for which a typedef foo const_iterator
exists in the same manner. I was trying to use the new C++11 template metaprogramming for this, however I could not find a method to detect if a certain type is missing.
Because I also need to turn on/off other template specializations based on other characteristics, I am currently using a template with two parameters, and the second one gets produced via std::enable_if
. Here is what I am currently doing:
template <typename T, typename Enable = void>
struct Foo{}; // default case is invalid
template <typename T>
struct Foo< T, typename std::enable_if<std::is_fundamental<T>::value>::type>{
void do_stuff(){ ... }
};
template<typename T>
struct exists{
static const bool value = true;
};
template<typename T>
struct Foo<T, typename std::enable_if<exists< typename T::const_iterator >::value >::type> {
void do_stuff(){ ... }
};
I was not able to do something like this without the exists
helper template. For example simply doing
template<typename T>
struct Foo<T, typename T::const_iterator> {
void do_stuff(){ ... }
};
did not work, because in those cases where this specialization should be used, the invalid default case was instantiated instead.
However I could not find this exists
anywhere in the new C++11 standard, which as far as I know simply is taking from boost::type_traits
for this kind of stuff. However on the homepage for boost::type_traits
does not show any reference to anything that could be used instead.
Is this functionality missing, or did I overlook some other obvious way to achieve the desired behavior?
You can create a trait has_const_iterator
that provides a boolean value and use that in the specialization.
Something like this might do it:
template <typename T>
struct has_const_iterator {
private:
template <typename T1>
static typename T1::const_iterator test(int);
template <typename>
static void test(...);
public:
enum { value = !std::is_void<decltype(test<T>(0))>::value };
};
And then you can specialize like this:
template <typename T,
bool IsFundamental = std::is_fundamental<T>::value,
bool HasConstIterator = has_const_iterator<T>::value>
struct Foo; // default case is invalid, so no definition!
template <typename T>
struct Foo< T, true, false>{
void do_stuff(){// bla }
};
template<typename T>
struct Foo<T, false, true> {
void do_stuff(){//bla}
};
If you simply want if a given type contains const_iterator
then following is a simplified version of your code:
template<typename T>
struct void_ { typedef void type; };
template<typename T, typename = void>
struct Foo {};
template<typename T>
struct Foo <T, typename void_<typename T::const_iterator>::type> {
void do_stuff(){ ... }
};
See this answer for some explanation of how this technique works.
Here's another version of a member type trait check:
template<typename T>
struct has_const_iterator
{
private:
typedef char yes;
typedef struct { char array[2]; } no;
template<typename C> static yes test(typename C::const_iterator*);
template<typename C> static no test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
There is a couple of ways to do this. In C++03, you could use boost and enable_if
to define the trait (docs, source):
BOOST_MPL_HAS_XXX_TRAIT_DEF(const_iterator);
template <typename T, typename Enable = void>
struct Foo;
template <typename T>
struct Foo< T, typename boost::enable_if<boost::is_fundamental<T> >::type>{
void do_stuff(){ ... }
};
template<typename T>
struct Foo<T, typename boost::enable_if<has_const_iterator<T> >::type> {
void do_stuff(){ ... }
};
In C++11, you could use Tick like this:
TICK_TRAIT(has_const_iterator)
{
template<class T>
auto require(const T&) -> valid<
has_type<typename T::const_iterator>
>;
};
template <typename T, typename Enable = void>
struct Foo;
template <typename T>
struct Foo< T, TICK_CLASS_REQUIRES(std::is_fundamental<T>::value)>{
void do_stuff(){ ... }
};
template<typename T>
struct Foo<T, TICK_CLASS_REQUIRES(has_const_iterator<T>())> {
void do_stuff(){ ... }
};
Also with Tick you can further enhance the trait to actually detect that the const_iterator
is actually an iterator, as well. So say we define a simple is_iterator
trait like this:
TICK_TRAIT(is_iterator,
std::is_copy_constructible<_>)
{
template<class I>
auto require(I&& i) -> valid<
decltype(*i),
decltype(++i)
>;
};
We can then define has_const_iterator
trait to check that the const_iterator
type matches the is_iterator
trait like this:
TICK_TRAIT(has_const_iterator)
{
template<class T>
auto require(const T&) -> valid<
has_type<typename T::const_iterator, is_iterator<_>>
>;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With