簡體   English   中英

為什么lexical_cast要求operator >>在匹配的命名空間中?

[英]Why does lexical_cast require the operator>> to be in a matching namespace?

這是一個測試用例:

#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),

演示

但是,當我在命名空間N聲明/定義operator>>時,代碼的工作方式與廣告一樣。

為什么? 為什么查找會失敗?

由於對operator>>的調用是由boost::lexical_cast<>函數模板構成的,因此operator>>的第二個參數是一個從屬名稱

查找規則

正如在lookup中所討論的那樣,模板中使用的從屬名稱的查找被推遲,直到模板參數已知,此時

  • 非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_operatorN::AC未在規定FakeBoost ,它也沒有定義N (所以沒有ADL),所以fake_cast不會找到它。

錯誤消息有點混亂(因為boost)。 我的例子是:

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

這解釋了很多。

一旦在namespace boost找到operator>> ,它就會停止查找封閉的命名空間。 但是,它也會進行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的定義時do_foo ),如果“已找到一個名為的函數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>>遵循基本相同的規則。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM