Sign in to follow this  
SiS-Shadowman

nested template class, overloaded operator not found

Recommended Posts

SiS-Shadowman    359
I've written a small test that demonstrates the problem. I've got a template for a class (A) that contains a template for a nested class (B). I want to define stream operators for both classes, however the compiler is never able to find the one I've defined for A::B:
template <typename T>
class A
{
public:

	template <typename O>
	class B
	{};
};

template <typename t>
class C
{};



template <typename Char, typename Traits, typename T>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const A<T>&)
{
	return stream << "A!";
}

template <typename Char, typename Traits, typename T, typename O>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const typename A<T>::B<O>&)
{
	return stream << "B in A!";
}

template <typename Char, typename Traits, typename T>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const C<T>&)
{
	return stream << "C!";
}



int main(int argc, char* argv[])
{
	std::cout << A<int>() << std::endl;
	std::cout << C<int>() << std::endl;
	std::cout << A<int>::B<int>() << std::endl;
}

Both cout << A<int>() and cout << C<int>() compile, however the compiler claims that there is no operator that takes a right-hand A<T>::B<O> operand. Is this an error on my part and is there something I can do about it? I think I need that behaviour because I'm writing a small generic class that stores sequences of elements and that provides views on that sequence (the view class is the nested one). I'm using Visual Studio 2008 without any SP.

Share this post


Link to post
Share on other sites
Makaan    100
I find the token "typename" in line

std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const typename A<T>::B<O>&)
to be very suspicious; you could try removing it.

Share this post


Link to post
Share on other sites
SiS-Shadowman    359
It raises an error when I do not add "typename", because the compiler doesn't know B is a type without it.
Quote:

warning C4346: 'A<T>::B<O>' : dependent name is not a type prefix with 'typename' to indicate a type
error C2061: syntax error : identifier 'B'

Share this post


Link to post
Share on other sites
dv    124
Changing


std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const typename A<T>::B<O>&)




to


std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const typename A<T>::template B<O>&)





would solve one part of the problem. The other one is that apparently it is not possible to deduce T this way. I succeeded by circumventing the nesting - just do an empty derivation of a base class, like this:


template <typename O>
class B1
{};


template <typename T>
class A
{
public:

template <typename O>
class B: public B1 < O >
{};
};

template <typename t>
class C
{};



template <typename Char, typename Traits, typename T>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const A<T>&)
{
return stream << "A!";
}

template <typename Char, typename Traits, typename T>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const B1<T>&)
{
return stream << "B in A!";
}

template <typename Char, typename Traits, typename T>
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const C<T>&)
{
return stream << "C!";
}



int main(int argc, char* argv[])
{
std::cout << A<int>() << std::endl;
std::cout << C<int>() << std::endl;
std::cout << A<int>::B<int>() << std::endl;
}





Note B1 and B. B is essentially just a "template typedef". A proper template typedef does not exist in C++98 (but will exist in C++0x), so I just do an "empty derivation", e.g. the derived class does not really add anything to the base class functionality.

Share this post


Link to post
Share on other sites
mattd    1078
AFAIK, this is because the compiler is unable to deduce the template parameters for your operator<< for A<T>::B<O>s. This is because the type T is used in a non-deduced context, as it is used as part of a nested type name.

So if you explicitly give the call its template parameters, it works:


template <typename T, typename O, typename Char, typename Traits> // note order
std::basic_ostream<Char, Traits>& operator<<(std::basic_ostream<Char, Traits>& stream, const typename A<T>::B<O>&)
{
return stream << "B in A!";
}

// ...

::operator<< <int, int>(std::cout, A<int>::B<int>()); // ok



Not entirely sure though, I'll leave it to the true C++ wizards to quote you C++ standard references and give a conclusive answer :)

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this