0

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.

Chris_F
  • 4,991
  • 5
  • 33
  • 63
  • Moving it into the boost namespace made no difference, however, moving it to std removed the errors. – Chris_F Nov 26 '18 at 08:38
  • 3
    Probably highly relevant if not a duplicate https://stackoverflow.com/questions/8111677/what-is-argument-dependent-lookup-aka-adl-or-koenig-lookup – StoryTeller - Unslander Monica Nov 26 '18 at 08:39
  • 1
    [This one](https://stackoverflow.com/q/51447860/9593596) is very close, too. – lubgr Nov 26 '18 at 08:40
  • @Chris_F Moving the overload into namespace `std` might work, but is technically not allowed. I would go with `std::cout << (boost::format("%d") % i.value_or(0));` as the default value is a bit context-dependent anyhow? – lubgr Nov 26 '18 at 08:41

1 Answers1

2

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.

felix
  • 2,213
  • 7
  • 16