Почему lexical_cast требует, чтобы оператор >> находился в соответствующем пространстве имен?

Вот тестовый пример:

#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 отклоняет преобразование, утверждая, что нет соответствия 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),

( демо)

Тем не менее, код работает как объявлено, когда я объявляю / определяю operator>> внутри пространства имен N,

Почему это? Почему поиск не дает результатов?

3 ответа

Решение

Поскольку призыв к operator>> сделан из boost::lexical_cast<> шаблон функции, второй аргумент operator>> является зависимым именем:

Правила поиска

Как обсуждалось при поиске, поиск зависимого имени, используемого в шаблоне, откладывается до тех пор, пока не будут известны аргументы шаблона, и в этот момент

  • поиск без ADL проверяет объявления функций с внешними связями, которые видны из контекста определения шаблона

  • ADL проверяет объявления функций с внешними связями, которые видны как из контекста определения шаблона, так и из контекста создания шаблона.

(другими словами, добавление нового объявления функции после определения шаблона не делает его видимым, кроме как через ADL)... Цель этого правила состоит в том, чтобы помочь предотвратить нарушения ODR для экземпляров шаблона.

Другими словами, поиск без ADL не выполняется из контекста создания шаблона.

Глобальное пространство имен не рассматривается, поскольку ни один из аргументов вызова не имеет никакого отношения к глобальному пространству имен.

operator>>(std::istream& is, N::alarm_code_t& code) не объявлено в пространстве имен Nследовательно, ADL не находит его.


Эти странности поиска имен описаны в явных пространствах имен N1691.

Я немного переписал пример:

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());
}

Сейчас fake_operator за N::AC не определен в FakeBoostэто также не определено в N (так нет ADL), так fake_cast не найду это.

Сообщение об ошибке немного сбивает с толку (потому что повышение). Для моего примера это:

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) {
      ^~~~~~~~~~~~~

Что многое объясняет.

Однажды operator>> находится в namespace boost, он перестает смотреть в замкнутых пространствах имен. Однако он также выполняет поиск ADL.

#include <iostream>

namespace B{
  struct bar {};
}

void foo(B::bar) {
  std::cout << "foobar!\n";
}


namespace A{
  void foo(int) {}

  template<class T>
  void do_foo( T t ) {
    foo(t);
  }
}


int main() {
  A::do_foo(B::bar{});
}

Выше не строит.

Закомментируйте void foo(int) {} и код компилируется. Ваша проблема такая же, только с операторами вместо foo,

В основном, операторы, не найденные ADL, чрезвычайно хрупки, на них нельзя полагаться.

живой пример.

Изменение порядка включения также нарушает поиск (если foo(B::bar) определяется после do_foo функция не может быть найдена в точке определения do_foo ни по ADL), если "уже найдена функция с именем foo (или же operator>>) не ломается. Это лишь часть того, что поиск в шаблонах без ADL хрупок.

Короче:

#include <iostream>


namespace A{

// void foo (int) {}

  template<class T>
  void do_foo( T t ) {
    foo(t);
  }
}

namespace B{
  struct bar {};
}

void foo(B::bar) {
  std::cout << "foobar!\n";
}

Также не строит с тем же mainпо состоянию на определение do_foo::foo не был виден, и он не будет найден через ADL, поскольку он не находится в пространстве имен, связанном с B::bar,

однажды foo перемещается в namespace bar оба случая работают.

operator>> следует в основном тем же правилам.

Другие вопросы по тегам