【问题标题】:Why does lexical_cast require the operator>> to be in a matching namespace?为什么 lexical_cast 要求 operator>> 在匹配的命名空间中?
【发布时间】:2016-08-15 14:55:14
【问题描述】:

这是一个测试用例:

#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&gt;&gt;

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)

但是,当我在命名空间 N 内声明/定义 operator&gt;&gt; 时,代码的工作方式与宣传的一样。

这是为什么呢?为什么查找会失败?

【问题讨论】:

  • 沼泽标准 ADL 问题?
  • @T.C.: 嗯...在命名空间N 中找到其他operator&gt;&gt;,因此未搜索全局命名空间并且未找到此特定operator&gt;&gt;?但我在N 中没有另一个operator&gt;&gt;。或者任何,事实上。我无法理解 ADL 的来源。
  • ADL 进来是因为你的operator&gt;&gt; 的第二个参数类型是N::alarm_code_t,所以N 是一个关联的命名空间,会搜索运算符定义。
  • @Praetorian &gt;&gt; 调用发生在 namespace boost。我认为 LRiO 的问题是“为什么在 namespace boost 中调用 &gt;&gt; 时不考虑 namespace :: 中的 operator&gt;&gt; ?”说“ADL 没有找到它”只回答了一半的问题:ADL 不是唯一的查找。

标签: c++ c++11 boost lexical-cast


【解决方案1】:

由于对operator&gt;&gt; 的调用是从boost::lexical_cast&lt;&gt; 函数模板进行的,所以operator&gt;&gt; 的第二个参数是dependent name

查找规则

正如查找中所讨论的,模板中使用的依赖名称的查找被推迟到知道模板参数之后,此时

  • 非 ADL 查找检查具有从 模板定义上下文可见的外部链接的函数声明

  • ADL 检查具有外部链接的函数声明,这些声明在 模板定义上下文模板实例化上下文

    中都可见

(换句话说,在模板定义之后添加一个新的函数声明不会使其可见,除非通过 ADL)...此规则的目的是帮助防止违反模板实例化的 ODR。

换句话说,非 ADL 查找不是从 模板实例化上下文中执行的

不考虑全局命名空间,因为调用的所有参数都与全局命名空间没有任何关联。

operator&gt;&gt;(std::istream&amp; is, N::alarm_code_t&amp; code) 没有在命名空间N 中声明,因此ADL 找不到它。


这些名称查找异常记录在 N1691 Explicit Namespaces 中。

【讨论】:

    【解决方案2】:

    我稍微重写了这个例子:

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

    这解释了很多。

    【讨论】:

    • "发现operator &gt;&gt; 的其他重载是因为它们在 boost 中使用了using namespace std;。"哇,什么?
    • @T.C.这似乎是错误的,我在实际调用 &gt;&gt; 的上下文中看不到 using namespace stdgithub.com/boostorg/lexical_cast/blob/develop/include/boost/…
    • @T.C.我删除了注释 - 它确实在其他情况下使用。但他们确实处理std - 如果你把fake_operator 放入std(仅用于科学)它开始工作
    • @Dadam 那是因为 ADL 会在 std::istream 或其他类型上找到它
    • @TavianBarnes 对,有 两个 参数可用于 ADL。谢谢!
    【解决方案3】:

    一旦在namespace boost 中找到operator&gt;&gt;,它就会停止在封闭的命名空间中查找。但是,它也会进行 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 找不到的运算符非常脆弱,您不能依赖它们。

    live example.

    包含顺序的更改也会中断查找(如果 foo(B::bar)do_foo 函数之后定义,则在 do_foo 的定义点或 ADL 都无法找到它),如果“已经找到一个名为 foo(或 operator&gt;&gt;)的函数不会破坏它。这只是模板中非 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&gt;&gt; 遵循基本相同的规则。

    【讨论】:

    • 即使namespace boost 包含零个operator&gt;&gt;s,::operator &gt;&gt; 仍然不会被普通的非限定查找找到,因为它只考虑模板定义上下文。
    • @t.c.如果您在包含标题之前定义了&gt;&gt;,它将被找到。 ;)
    猜你喜欢
    • 2015-02-17
    • 2020-08-07
    • 2020-11-24
    • 1970-01-01
    • 2015-11-13
    • 2016-12-10
    • 2022-07-06
    • 2016-04-07
    • 1970-01-01
    相关资源
    最近更新 更多