Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

type signature of an enable_if'ed templated template constructor?

I usually declare my classes and templates, and then define their methods after (in the same header file, of course). I just find it easier to read that way. Well, I've come across a case where I can't figure out a working type signature to use in an out-of-class definition. Here's a simplified example of what I'm doing, that illustrates the problem:

template <class T>
struct Foo
  {
    Foo(T a, T b);

    template 
      < class Iterator
      , enable_if< is_iterator<Iterator> >
      >
    Foo
      ( Iterator first
      , Iterator last
      );
  };

template <class T>
Foo<T>::Foo(T a, T b)
{ ... }

template <class T>
template
  < class U
  , WHAT_GOES_HERE?
  >
Foo<T>::Foo(U f, U l)
{ ... }

I have tried a number of things in the WHAT_GOES_HERE slot to try to get a matching signature, and I keep failing. I need the enable_if to distinguish the case where one passes in two objects of type T, and when one passes in a pair of Iterators. The code works fine if the templated constructor is defined inside the main template, which is how the code currently does it, but I'd much rather move the definition outside the declaration.

EDIT: I should mention that I can't just re-use enable_if<...> in the definition, because enable_if<...> assigns a default value for its type, which you cannot do in a definition that isn't also a declaration.

like image 677
swestrup Avatar asked Feb 27 '11 20:02

swestrup


3 Answers

I wouldn't do it that way. Here's the changes I would make:

template <class T>
struct Foo
  {
    Foo(T a, T b);

    template 
      < class Iterator
      >
    Foo
      ( Iterator first
      , Iterator last
      , typename enable_if<is_iterator<Iterator> >::type* = 0
      );
  };

template <class T>
Foo<T>::Foo(T a, T b)
{ ... }

template <class T>
template
  < class U
  >
Foo<T>::Foo(U f, U l, typename enable_if< is_iterator<U> >::type*)
{ ... }

This is straight out of the documentation for enable_if.

like image 194
Edward Strange Avatar answered Nov 07 '22 09:11

Edward Strange


Is this what you are trying to accomplish? [I don't have an is_iterator type trait, so I've reworked your example using the C++0x type traits and utility libraries. It should work the same way with the TR1 and Boost libraries.]

#include <utility>
#include <type_traits>

template <typename T>
struct S
{
    // Constructor (1)
    S(T, T); 

    // Constructor (2)
    template <typename U>
    S(U, U, typename std::enable_if<std::is_integral<U>::value>::type* = 0);
};

template <typename T>
S<T>::S(T, T)
{ }

template <typename T>
template <typename U>
S<T>::S(U, U, typename std::enable_if<std::is_integral<U>::value>::type*)
{ }

int main()
{
    S<double> a(1.0, 2.0); // uses (1)
    S<double> b(1, 2);     // uses (2)
}
like image 44
James McNellis Avatar answered Nov 07 '22 10:11

James McNellis


The simplest you can do is:

template<class Iterator>
Foo
  ( Iterator first
  , typename enable_if<is_iterator<Iterator>, Iterator>::type last
  );
like image 35
Maxim Egorushkin Avatar answered Nov 07 '22 09:11

Maxim Egorushkin