I want to be able to use a std::optional<int>
with Boost.Format.
#include <iostream>
#include <optional>
#include <boost/format.hpp>
struct SomeType
{
int x;
};
std::ostream& operator<<(std::ostream& os, const SomeType& t)
{
os << t.x;
return os;
}
std::ostream& operator<<(std::ostream& os, const std::optional<int>& t)
{
os << t.value_or(0);
return os;
}
void test()
{
SomeType t{42};
std::cout << (boost::format("%s") % t); //this is fine
std::optional<int> i = 42;
std::cout << (boost::format("%s") % i); //nope
}
The code above gives me the following compiler error:
opt/compiler-explorer/libs/boost_1_68_0/boost/format/feed_args.hpp:99:12: error: no match for 'operator<<' (operand types are 'std::basic_ostream<char>' and 'const std::optional<int>')
os << x ;
~~~^~~~
There are no compiler errors if I simply pass i
directly to std::cout
.
boost::format("%s") % i
invokes a call to operator<<
. Name lookup rule is followed during compiling to find a operator<<
.
For boost::format("%s") % t
, both struct SomeType
and std::ostream& operator<<(std::ostream& os, const SomeType& t)
is defined in global namespace, by using ADL, operator<<
is found.
For (boost::format("%s") % i)
, std::optional
is defined in namespace std
, but corresponding operator<<
is defined in global namespace. By using ADL, boost won't be able to find it. And
non-ADL lookup examines function declarations with external linkage that are visible from the template definition context,
so the compiler isn't able to find the operator<<
which you defined.
A workaround: wrap std::optional inside your own ReferenceWrapper, then define the inserter for your wrapper in the same namespace where ReferenceWrapper is defined.