11

这是一个测试用例:

#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>>在 namespace中声明/定义时,代码的工作方式与宣传的一样N

为什么?为什么查找会失败?

4

3 回答 3

15

由于调用operator>>是从boost::lexical_cast<>函数模板进行的,所以第二个参数operator>>是一个从属名称

查找规则

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

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

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

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

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

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

operator>>(std::istream& is, N::alarm_code_t& code)未在 namespace 中声明N,因此ADL找不到它。


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

于 2016-08-15T15:15:15.323 回答
4

我稍微重写了这个例子:

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

这解释了很多。

于 2016-08-15T15:22:15.170 回答
2

一旦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_fooADL 的定义点找到它),如果“已经找到名为foo(or operator>>) 的函数不会破坏它。这只是模板中的非 ADL 查找很脆弱的许多方式的一部分。

简而言之:

#include <iostream>


namespace A{

// 无效 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 ::fooB::bar

一旦foo被移动到namespace bar两种情况下工作。

operator>>遵循基本相同的规则。

于 2016-08-15T15:22:49.613 回答