Limiting classes that a template parameter can be interpreted as

C

Chris Swiedler

How do I limit a template parameter to a specific set of classes? If I
write code like

class MyStream1
{
public:
void Write(...) {}
};

class MyStream2
{
public:
void Write(...) {}
};

template <class S> S & operator<<(S &lhs, int rhs) { lhs.Write(rhs);
return lhs; }

MyStream1 ms1;
MyStream2 ms2;
ms1 << 5;
ms2 << 5;
std::cout << 5;

.... then the compiler complains that ostream doesn't have a Write
method, i.e. it's using my overloaded free operator<< to stream into
an ostream, which is obviously bad. I then tried:

class MyStream1
{
public:
typedef MyStream1 StreamType;

void Write(...) {}
};

class MyStream2
{
public:
typedef MyStream2 StreamType;

void Write(...) {}
};

template <class S> class S::StreamType & operator<< (class
S::StreamType &lhs, int rhs) { lhs.Write(rhs); return lhs;}

MyStream1 ms1;
MyStream2 ms2;
ms1 << 5;
ms2 << 5;
std::cout << 5;

When I do this, it no longer tries to use my overload for the ostream,
but instead complains that there is no valid overloaded operator<< for
MyStream1 or MyStream2. Is this nested typedef a valid way to limit my
overload to my own classes? Is there another way to do this?

I've actually switched away from this technique to using a base class
which implements Write(), and having the operator<< take the base
class as its first parameter. Still, I'm curious how I might
accomplish this.

chris
 
A

Alf P. Steinbach

* Chris Swiedler:
How do I limit a template parameter to a specific set of classes? If I
write code like

class MyStream1
{
public:
void Write(...) {}
};

class MyStream2
{
public:
void Write(...) {}
};

template <class S> S & operator<<(S &lhs, int rhs) { lhs.Write(rhs);
return lhs; }

MyStream1 ms1;
MyStream2 ms2;
ms1 << 5;
ms2 << 5;
std::cout << 5;

... then the compiler complains that ostream doesn't have a Write
method, i.e. it's using my overloaded free operator<< to stream into
an ostream, which is obviously bad. I then tried:

class MyStream1
{
public:
typedef MyStream1 StreamType;

void Write(...) {}
};

class MyStream2
{
public:
typedef MyStream2 StreamType;

void Write(...) {}
};

template <class S> class S::StreamType & operator<< (class
S::StreamType &lhs, int rhs) { lhs.Write(rhs); return lhs;}

That would need to be

template< class S >
typename S::StreamType& operator<<(
typename S::StreamType& lhs, int rhs
);

MyStream1 ms1;
MyStream2 ms2;
ms1 << 5;
ms2 << 5;
std::cout << 5;

When I do this, it no longer tries to use my overload for the ostream,
but instead complains that there is no valid overloaded operator<< for
MyStream1 or MyStream2. Is this nested typedef a valid way to limit my
overload to my own classes?

No, not even if you defined operator<< in a technically correct way as
shown above.

Template parameter deduction fails.

Is there another way to do this?

I've actually switched away from this technique to using a base class
which implements Write(), and having the operator<< take the base
class as its first parameter. Still, I'm curious how I might
accomplish this.

You don't need to use boost::enable_if.

Just add a namespace:

#include <iostream>

namespace my
{
class Stream1
{
public:
void Write(...) {}
};

class Stream2
{
public:
void Write(...) {}
};

template< class S >
S& operator<<( S& lhs, int rhs )
{
lhs.Write( rhs ); return lhs;
}
}

int main()
{
my::Stream1 ms1;
my::Stream2 ms2;
ms1 << 5;
ms2 << 5;
std::cout << 5;
}

The prefix "My" you had indicates a namespace anyway...


Cheers, & hth.,

- Alf
 
C

Chris Swiedler

template said:
That would need to be

template< class S >
typename S::StreamType& operator<<(
typename S::StreamType& lhs, int rhs
);

Yes, that's what I meant, thanks.
You don't need to use boost::enable_if.

Just add a namespace:

#include <iostream>

namespace my
{
class Stream1
{
public:
void Write(...) {}
};

class Stream2
{
public:
void Write(...) {}
};

template< class S >
S& operator<<( S& lhs, int rhs )
{
lhs.Write( rhs ); return lhs;
}
}

I'm confused as to why this helps. How do namespaces affect template
parameter resolution and function overload choice? Are template
parameters in free functions which are defined in a namespace only
allowed to resolve to types in that namespace? Are free function
overloads of operator<< only used if they're in the same namespace as
the code which invokes them?

thanks,
chris
 
A

Alf P. Steinbach

* Chris Swiedler:
Yes, that's what I meant, thanks.


I'm confused as to why this helps. How do namespaces affect template
parameter resolution and function overload choice? Are template
parameters in free functions which are defined in a namespace only
allowed to resolve to types in that namespace? Are free function
overloads of operator<< only used if they're in the same namespace as
the code which invokes them?

With the whole thing in a namespace, and no "using...", the above
operator<< is only found via argument-dependent lookup (ADL). And ADL
doesn't find it for use of operator<< with the standard iostreams.

Cheers, & hth.,

- Alf
 
J

Juha Nieminen

Chris said:
How do I limit a template parameter to a specific set of classes?

If I'm not completely mistaken, the upcoming C++ standard will define
new syntax for this exact purpose. (Its main goal is to make it easier
for compilers to generate legible error messages when something which
requires iterators is given the wrong type of iterator (or something
which is not an iterator at all).)
 
C

Chris Swiedler

With the whole thing in a namespace, and no "using...", the above
operator<< is only found via argument-dependent lookup (ADL). And ADL
doesn't find it for use of operator<< with the standard iostreams.

If I understand correctly, in the case of templated parameters, the
actual type of the parameter passed in is used to find the correct
free function? Which is why I get the following:

class A { };

namespace NS
{

class B { };

template <typename T> void f( T t ) {}

void works()
{
::A a;
NS::B b;
f(a);
f(b);
}

};

void doesnt_work()
{
::A a;
NS::B b;
f(a); // error, can't find f() which takes an ::A
f(b);
}

Therefore, if I define my free operator<< function in a namespace,
code outside the namespace will not find it, but anything inside the
namespace will. That explains my actual problem, which was with some
code which streamed into a stringstream, located inside the same
namespace as the operator<<.

thanks,
chris
 
A

Alf P. Steinbach

* Chris Swiedler:
If I understand correctly, in the case of templated parameters,

Well, ADL is not restricted to templates.

It's the same for ordinary functions.

the
actual type of the parameter passed in is used to find the correct
free function?

Yes.


Cheers,

- Alf
 

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
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top