2016-08-15 21 views
9

Oto testcase:Dlaczego funkcja lexical_cast wymaga, aby operator >> znajdował się w pasującym obszarze nazw?

#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"); 
} 

doładowania odrzuca konwersję, twierdząc, że nie ma dopasowania 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)

Jednak kod działa jak reklamowane kiedy zadeklaruj/zdefiniuj operator>> wewnątrz przestrzeni nazw N.

Dlaczego tak jest? Dlaczego wyszukiwanie kończy się niepowodzeniem?

+7

Bog-standardowy problem z ADL? –

+0

@ T.C .: Hmm ... inne 'operator >>' znalezione w przestrzeni nazw 'N' więc nie przeszukano globalnej przestrzeni nazw i nie znaleziono tego konkretnego' operatora >> '? Ale nie mam innego 'operatora >>' w 'N'. Lub w rzeczywistości. Nie mogę zgadnąć, skąd się bierze ADL. –

+1

ADL wchodzi w to, ponieważ twoim drugim parametrem 'operator >>' jest 'N :: alarm_code_t', więc' N' jest skojarzoną przestrzenią nazw i zostanie przeszukana dla definicji operatora. – Praetorian

Odpowiedz

13

Ponieważ wywołanie operator>> jest wykonany z boost::lexical_cast<> funkcji szablonu, drugi argument operator>> jest dependent name:

zasady Lookup

Jak omówiono w wyszukiwarkę, odnośnika o nazwie zależnej od stosowanego w szablon jest przełożone są znane argumenty szablonu, w którym to czasie

  • non-ADL loo kup bada zgłoszenia funkcyjne z zewnętrznych łącznika, który jest widoczny z kontekście rozdzielczości szablonu

  • ADL bada zgłoszenia funkcyjne z łącznikiem zewnętrznym, które są widoczne zarówno w kontekście rozdzielczości szablonu i kontekstu tworzenia instancji szablonu

(innymi słowy, dodanie nowej deklaracji funkcji po definicji szablonu nie czyni jej widoczną, z wyjątkiem poprzez ADL) ... Celem tej reguły jest ochrona przed naruszeniem ODR dla szablonu wystąpienia.

Innymi słowy, zakaz ADL wyszukiwanie nie jest wykonywana z szablonu kontekście konkretyzacji.

Globalny obszar nazw nie jest uwzględniany, ponieważ żaden z argumentów wywołania nie ma żadnego związku z globalną przestrzenią nazw.

operator>>(std::istream& is, N::alarm_code_t& code) nie jest zadeklarowany w obszarze nazw N, stąd ADL go nie znajduje.


Te dziwactwa wyszukiwania nazwiska są udokumentowane w N1691 Explicit Namespaces.

4

przepisałem przykładzie trochę:

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

Teraz fake_operator dla N::AC nie jest zdefiniowane w FakeBoost, to nie jest również zdefiniowane w N (więc nie ADL), więc fake_cast nie będzie go znaleźć.

Komunikat o błędzie jest nieco kłopotliwy (ponieważ zwiększenie). Dla mojego przykładu jest to:

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

Co bardzo wyjaśnia.

+1

"Znaleziono inne przeciążenia operatora' ', ponieważ używają' using namespace std; 'in boost." whoa, co? –

+1

@ T.C. To wydaje się błędne, nie widzę "using namespace std" w kontekście, który faktycznie wywołuje '>>': https://github.com/boostorg/lexical_cast/blob/develop/include/boost/lexical_cast/detail/converter_lexical_streams.hpp # L601 –

+1

@TC Usunąłem notatkę - jest ona naprawdę używana w innych kontekstach.Ale radzą sobie z 'std' - jeśli wstawisz' fake_operator' na 'std' (tylko dla nauki), to zacznie działać –

2

Po znalezieniu operator>> w namespace boost przestaje wyglądać w zamkniętych przestrzeniach nazw. Wykonuje jednak również wyszukiwanie 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{}); 
} 

Powyższe nie buduje.

Skomentuj void foo(int) {} i kod się kompiluje. Twój problem jest taki sam, tylko z operatorami zamiast z foo.

Zasadniczo operatory nie znalezione przez ADL są wyjątkowo delikatne, nie można na nich polegać.

live example.

Zmiana zawierać porządek łamie również odnośnika (jeśli foo(B::bar) jest zdefiniowana po funkcji do_foo, że nie można znaleźć w punkcie definicji do_foo ani przez ADL), jeśli „już znaleźć funkcję o nazwie foo (. lub operator>>) nie złamać to tylko część z wielu sposobów non-ADL wyszukiwanie w szablonach jest kruchy

w skrócie:.

#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"; 
} 

także nie tworzy z tą samą main, na zaliczane do do_foo::foo nie był widoczny i nie zostaną znalezione poprzez ADL, ponieważ nie jest w nazw związanego z B::bar.

Po przeniesieniu foo do namespace bar oba przypadki działają.

operator>> podąża zasadniczo za tymi samymi regułami.

+1

Nawet jeśli 'boost przestrzeni nazw' zawiera zero' operator >> 's,' :: operator >> 'nadal nie zostanie znalezione przez zwykłe niewykwalifikowane wyszukiwanie, ponieważ uwzględnia ono tylko kontekst definicji szablonu. –

+0

@ t.c. jeśli zdefiniowałeś '>>' przed dołączeniem nagłówka, to by go znaleziono. ;) – Yakk