I have a type in a nested namespace (abc::util
) and its overloaded operator<<
. However, trying to call it from the outer namespace (abc
) causes a compile error:
<source>: In function 'void abc::func(std::ostringstream&, const Foo&)':
<source>:38:9: error: no match for 'operator<<' (operand types are 'std::basic_ostream<char>' and 'const abc::Foo::MyType' {aka 'const abc::util::MyType'})
37 | oss << ';'
| ~~~~~~~~~~
| |
| std::basic_ostream<char>
38 | << foo.value;
| ^~ ~~~~~~~~~
| |
| const abc::Foo::MyType {aka const abc::util::MyType}
Here is the minimal example:
#include <sstream>
#include <string>
#include <print>
namespace abc::util
{
struct MyType
{
std::string value;
};
std::ostringstream&
operator<<( std::ostringstream& oss, const MyType& some_value )
{
oss << some_value.value;
return oss;
}
}
namespace abc
{
struct Foo
{
using MyType = util::MyType;
MyType value { "12345" };
};
void
func( std::ostringstream& oss, const Foo& foo )
{
oss << ';'
<< foo.value; // does not compile
// oss << ';';
// oss << foo.value; compiles fine
}
}
int main( )
{
std::ostringstream oss;
abc::func( oss, abc::Foo { } );
std::println( "{}", oss.view( ) );
}
I don't exactly know why this compiles:
oss << ';';
oss << foo.value;
but this doesn't:
oss << ';'
<< foo.value;
Any reasons?
The problem:
oss << ';'
returns ostream&
and not ostringstream&
and so you cannot chain the calls as you attempt with:
oss << ';' << foo.value;
Note that the namespaces are not relevant to this problem.
A solution:
A valid solution would be to change your operator<<
and func
to use std::ostream
instead of std::ostringstream
.
I.e.:
Change:
std::ostringstream& operator<<( std::ostringstream& oss, const MyType& some_value)
To:
std::ostream& operator<<( std::ostream& oss, const MyType& some_value)
And change:
void func(std::ostringstream& oss, const Foo& foo)
To:
void func(std::ostream& oss, const Foo& foo)
Note that you can still use these functions from main()
with std::ostringstream oss
, since ostringstream
is derived from ostream
.
Therefore this solution will only make your code more general.
A final note:
You mentioned in the comments that you don't need to make these types printable using things like std::cout
or std::ofstream
, but I don't see why it is an argument against this solution:
Making your code more general is usually a positive thing.
The fact that you don't need it (at the moment or even ever) does not change this.
And in this specific case the more general implemenetation is very easy to achieve.
Of course there could be specific reasons to avoid generality (which is why I wrote "usually"), but you didn't specify any.