Re: template & specialization
* 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?
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?