У меня есть тип во вложенном пространстве имен (abc::util
), и он перегружен operator<<
. Однако попытка вызвать его из внешнего пространства имен (abc
) приводит к ошибке компиляции:
<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}
Вот минимальный пример:
#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( ) );
}
Я точно не знаю, почему это компилируется:
oss << ';';
oss << foo.value;
но это не так:
oss << ';'
<< foo.value;
Есть причины?
Пространства имен не имеют значения. - там написано, что «типы операндов: 'std::basic_ostream<char>'...», и это не соответствует вашему прототипу. См. здесь по той же проблеме, только с глобальными именами. Есть ли у вас какая-то особая причина использовать только ostringstream
?
Если вы измените ostringstream
на ostream
в func
и в operator<<
, это будет работать (oss
в main
может остаться ostringstream
). Это также сделает их более общими. Есть ли причина не делать этого?
@wohlstad Мне не нужно делать эти типы пригодными для печати, используя такие вещи, как std::cout или std::ofstream, поэтому я определил только перегрузки для ostringstream и istringstream.
@digito_evo Я понимаю, но сделать ваш код более общим — это в целом хорошо. И в этом случае реализовать его не сложнее, плюс он решит вашу текущую проблему. Так что, ИМХО, у вас должна быть веская причина избегать этого.
Добавил полный ответ, так как комментарии здесь эфемерны.
Проблема:
oss << ';'
возвращает ostream&
, а не ostringstream&
, поэтому вы не можете связывать вызовы при попытке:
oss << ';' << foo.value;
Обратите внимание, что пространства имен не имеют отношения к этой проблеме.
Решение:
Допустимым решением было бы изменить operator<<
и func
на использование std::ostream
вместо std::ostringstream
.
То есть:
Изменять:
std::ostringstream& operator<<( std::ostringstream& oss, const MyType& some_value)
К:
std::ostream& operator<<( std::ostream& oss, const MyType& some_value)
И изменить:
void func(std::ostringstream& oss, const Foo& foo)
К:
void func(std::ostream& oss, const Foo& foo)
Обратите внимание, что вы все равно можете использовать эти функции из main()
с std::ostringstream oss
, поскольку ostringstream
является производным от ostream
.
Поэтому это решение только сделает ваш код более общим.
Последнее замечание:
В комментариях вы упомянули, что вам не нужно делать эти типы печатаемыми, используя такие вещи, как std::cout
или std::ofstream
, но я не понимаю, почему это является аргументом против этого решения:
Создание более общего кода обычно является положительным моментом.
Тот факт, что вам это не нужно (на данный момент или даже когда-либо), этого не меняет.
И в этом конкретном случае очень легко добиться более общей реализации.
Конечно, могли быть конкретные причины избегать обобщений (поэтому я и написал «обычно»), но вы их не указали.
Основная причина в том, что я не хочу, чтобы код намеренно или непреднамеренно записывал эти типы в файл через ofstream.
Вы можете добавить assert(dynamic_cast<std::ostringstream&>(oss));
внутри func
. Я признаю, что это не идеально и проблема будет обнаружена только во время выполнения, но на тот случай, если у вас нет лучшего решения.
Что делает динамическое приведение типов в утверждении? Должен ли он выбрасываться при забросе?
dynamic_cast<std::ostringstream&>
вернет ноль, если oss
не является ссылкой на ostringstream
(или не является производным от него), а затем утверждение выскочит (поскольку оно эквивалентно false). Вы можете попробовать это, пройдя, например. std::ofstream
. Как я уже сказал, меня не слишком устраивает это предложение, и решение на этапе компиляции (если вы его нашли) было бы лучше.
На самом деле мне кажется, что это нормально. Утверждение отключено в сборках выпуска, поэтому меня устраивает это решение. Спасибо.
Проблема в том, что
oss << ';'
возвращаетostream&
, а неostringstream&
. Это разрывает цепочку.