template & specialization

  • Thread starter Glenn G. Chappell
  • Start date
G

Glenn G. Chappell

I am trying to write two constructors for the same class. One takes an
iterator and so is a template. The other takes a particular type by
reference to const.

class Foo {
public:
template<typename InputIterator>
Foo(InputIterator i);
Foo(const Bar & b);

When I do this, trying to construct a Foo from a Bar calls the
template, not the constructor from a Bar. This is in VC++ 7.0. And it
does not matter which I declare first.

Questions: Is this a compiler oddity or standard behavior? Is there any
way around it?

I know a semi-solution. When I pass them both by value or both by
reference to const, it works.

template<typename InputIterator>
Foo(InputIterator i);
Foo(Bar b);

or

template<typename InputIterator>
Foo(const InputIterator & i);
Foo(const Bar & b);

But I would prefer not to do that. Any other ideas/thoughts?
 
V

Victor Bazarov

Glenn said:
I am trying to write two constructors for the same class. One takes an
iterator and so is a template. The other takes a particular type by
reference to const.

class Foo {
public:
template<typename InputIterator>
Foo(InputIterator i);
Foo(const Bar & b);

When I do this, trying to construct a Foo from a Bar calls the
template, not the constructor from a Bar. This is in VC++ 7.0. And it
does not matter which I declare first.

Questions: Is this a compiler oddity or standard behavior? Is there
any way around it?

It is a standard behaviour. If you construct from an object, the template
one will win because the compiler will figure out what type it is from
the argument and there will be no conversion required. A const reference
requires "binding to".
I know a semi-solution. When I pass them both by value or both by
reference to const, it works.

template<typename InputIterator>
Foo(InputIterator i);
Foo(Bar b);

or

template<typename InputIterator>
Foo(const InputIterator & i);
Foo(const Bar & b);

But I would prefer not to do that. Any other ideas/thoughts?

You can add a fake argument to the one you don't care to keep implicit:

template<typename InputIterator>
Foo(InputIterator i, int);

Of course, you will have to stick an extra argument when constructing
an object from the iterator...

V
 
A

Alf P. Steinbach

* Glenn G. Chappell:
I am trying to write two constructors for the same class. One takes an
iterator and so is a template. The other takes a particular type by
reference to const.

class Foo {
public:
template<typename InputIterator>
Foo(InputIterator i);
Foo(const Bar & b);

When I do this, trying to construct a Foo from a Bar calls the
template, not the constructor from a Bar. This is in VC++ 7.0. And it
does not matter which I declare first.

With both MSVC 7.1 and MingW g++ 3.4.2, the following program,

#include <iostream> // std::cout
#include <ostream> // operator<<, std::endl

void say( char const s[] ) { std::cout << s << std::endl; }

class Bar {};

class Foo
{
public:
template< typename InputIterator >
Foo( InputIterator ) { say( "Template" ); }

Foo( Bar const& ) { say( "Non-template" ); }
};

int main()
{
Bar b;
Foo f( b );
}

says "Non-template", but when passed an argument of type derived from
Bar it says "Template".

Questions: Is this a compiler oddity or standard behavior?

Off-hand I'd say a compiler oddity for the case of pure Bar argument.

Is there any way around it?

Best, use the solution you outline below. Second best (but should be
done anyway), upgrade the compiler. Third, perhaps use SFINAE
(Substitution Failure Is Not An Error).

The Boost library has a ready-made SFINAE solution. The following might
give you an idea if you don't want to use Boost:

#include <iostream> // std::cout
#include <ostream> // operator<<, std::endl

void say( char const s[] ) { std::cout << s << std::endl; }

class Bar {};
class BarD: public Bar {};

template< typename InputIterator >
struct NonBar { typedef InputIterator T; };

template<> struct NonBar<Bar>;
template<> struct NonBar<BarD>;

class Foo
{
public:
template< typename InputIterator >
Foo( InputIterator, typename NonBar<InputIterator>::T* = 0 )
{ say( "Template" ); }

Foo( Bar const& ) { say( "Non-template" ); }
};

int main()
{
Bar b;
BarD bd;

Foo f( b );
Foo fd( bd );
Foo fi( 666 );
}

with output "Non-template, non-template, template" with both MSVC 7.1
and MingW g++ 3.4.2.

Of course to make it more general you'd have to check sub-subclass
relationship and somehow make that part of the SFINAE checking (not sure
whether that's possible, perhaps the Boost solution does that), and
anyway, as I wrote above, consider solutions (1) and (2) first.

Solution (1):

I know a semi-solution. When I pass them both by value or both by
reference to const, it works.

template<typename InputIterator>
Foo(InputIterator i);
Foo(Bar b);

or

template<typename InputIterator>
Foo(const InputIterator & i);
Foo(const Bar & b);

But I would prefer not to do that. Any other ideas/thoughts?

I think this is the cleanest solution.

Why do you want to avoid this?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top