Here's a testcase:
#include <istream>
#include <boost/lexical_cast.hpp>
namespace N {
enum class alarm_code_t {
BLAH
};
}
std::istream& operator>>(std::istream& is, N::alarm_code_t& code)
{
std::string tmp;
is >> tmp;
if (tmp == "BLAH")
code = N::alarm_code_t::BLAH;
else
is.setstate(std::ios::failbit);
return is;
}
int main()
{
auto code = boost::lexical_cast<N::alarm_code_t>("BLAH");
}
Boost rejects the conversion, claiming that there's no matching operator>>
:
In file included from /usr/local/include/boost/iterator/iterator_categories.hpp:22:0,
from /usr/local/include/boost/iterator/iterator_facade.hpp:14,
from /usr/local/include/boost/range/iterator_range_core.hpp:27,
from /usr/local/include/boost/lexical_cast.hpp:30,
from main.cpp:2:
/usr/local/include/boost/lexical_cast/detail/converter_lexical.hpp: In instantiation of 'struct boost::detail::deduce_target_char_impl<boost::detail::deduce_character_type_later<N::alarm_code_t> >':
/usr/local/include/boost/lexical_cast/detail/converter_lexical.hpp:270:89: required from 'struct boost::detail::deduce_target_char<N::alarm_code_t>'
/usr/local/include/boost/lexical_cast/detail/converter_lexical.hpp:404:92: required from 'struct boost::detail::lexical_cast_stream_traits<const char*, N::alarm_code_t>'
/usr/local/include/boost/lexical_cast/detail/converter_lexical.hpp:465:15: required from 'struct boost::detail::lexical_converter_impl<N::alarm_code_t, const char*>'
/usr/local/include/boost/lexical_cast/try_lexical_convert.hpp:174:44: required from 'bool boost::conversion::detail::try_lexical_convert(const Source&, Target&) [with Target = N::alarm_code_t; Source = char [5]]'
/usr/local/include/boost/lexical_cast.hpp:42:60: required from 'Target boost::lexical_cast(const Source&) [with Target = N::alarm_code_t; Source = char [5]]'
main.cpp:25:60: required from here
/usr/local/include/boost/lexical_cast/detail/converter_lexical.hpp:243:13: error: static assertion failed: Target type is neither std::istream`able nor std::wistream`able
BOOST_STATIC_ASSERT_MSG((result_t::value || boost::has_right_shift<std::basic_istream<wchar_t>, T >::value),
(demo)
However, the code works as advertised when I declare/define operator>>
inside namespace N
.
Why's that? Why does the lookup otherwise fail?
Since the call to operator>>
is made from boost::lexical_cast<>
function template, the second argument to operator>>
is a dependent name:
Lookup rules
As discussed in lookup, the lookup of a dependent name used in a template is postponed until the template arguments are known, at which time
non-ADL lookup examines function declarations with external linkage that are visible from the template definition context
ADL examines function declarations with external linkage that are visible from both the template definition context and the template instantiation context
(in other words, adding a new function declaration after template definition does not make it visible, except via ADL)... The purpose of this is rule is to help guard against violations of the ODR for template instantiations.
In other words, non-ADL lookup is not performed from the template instantiation context.
The global namespace is not considered because none of the arguments of the call have any association with the global namespace.
operator>>(std::istream& is, N::alarm_code_t& code)
is not declared in namespace N
, hence ADL does not find it.
These name-lookup oddities are documented in N1691 Explicit Namespaces.
I rewrote the example a bit:
namespace N {
struct AC {};
}
namespace FakeBoost {
template <typename T>
void fake_cast(T t) {
fake_operator(t);
}
}
void fake_operator(N::AC ac) {
}
int main(){
FakeBoost::fake_cast(N::AC());
}
Now fake_operator
for N::AC
is not defined in FakeBoost
, it's also not defined in N
(so no ADL), so fake_cast
won't find it.
The error message is a bit consusing (because boost). For my example it is:
main.cpp: In instantiation of 'void FakeBoost::fake_cast(T) [with T = N::AC]':
main.cpp:19:33: required from here
main.cpp:10:22: error: 'fake_operator' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
fake_operator(t);
~~~~~~~~~~~~~^~~
main.cpp:14:6: note: 'void fake_operator(N::AC)' declared here, later in the translation unit
void fake_operator(N::AC ac) {
^~~~~~~~~~~~~
Which explains a lot.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With