【问题标题】:c++11 tie name clash with boostc++11 tie name 与 boost 冲突
【发布时间】:2013-11-18 23:44:46
【问题描述】:

我正在尝试将一些代码从boost::tuple 迁移到std::tuple,但我遇到了一些奇怪的错误:在我调用using namespace std(而不是boost)之后,我希望解决一个不合格的tie到std::tie。但是,例如,当元组包含 boost 容器指针时,这似乎会失败。

#include <tuple>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>

#ifdef USE_STD
#define TIE std::tie
#else
#define TIE tie
#endif

typedef boost::multi_index_container<
  int,
  boost::multi_index::indexed_by<
    boost::multi_index::ordered_non_unique<
      boost::multi_index::identity<int>
    >
  >
> Set;

std::tuple< int, int* > make_i_ptr();
std::tuple< int, Set* > make_set();

int main()
{
    using namespace std;
    int i;
    int* i_ptr;
    Set* set_ptr;
    tie(i, i_ptr) = make_i_ptr();
    TIE(i, set_ptr) = make_set();
    return 0;
}

如果我用g++ -std=c++0x -c test.cpp -DUSE_STD 编译,一切都很好。但是,如果没有-D USE_STD,我会收到提示g++ 尝试使用boost::tuples::tie 的编译错误。我正在使用 g++ 4.8.1 和 boost 1.55.0。你认为这是boost的错误吗?还是我缺少一些规范?

【问题讨论】:

  • 这是 ADL。对两者都使用限定名称。
  • "在我调用 using namespace std 之后" - NO
  • @Xeo:我不在头文件中。即,IRL,fcn 定义在没有using 的头文件中,这就是为什么我把它放在main() 这里。除此之外,这是一种偏好而非规则。
  • 你说“医生,我这样做会很痛。”医生说“不要那样做”。这确实是事情的工作方式(而不是 Boost 中的错误)。
  • @chris:谢谢,我不知道 ADL。但我不明白:听起来 ADL 应该帮助编译器解析名称。如果 ADL 让它解析为 boost::tie for...“我的方便”,然后编译失败,这不应该是编译器选择错误函数的线索吗?!

标签: c++ boost c++11 boost-tuples stdtuple


【解决方案1】:

查找很复杂。其他人提到的问题是参数依赖查找或 ADL。添加了 ADL 规则以允许在与它们引用的类型相同的命名空间中定义运算符,并启用查找以在存在时找到这些运算符。这后来扩展到标准化过程中的所有其他功能。

这里的问题是tie(...) 是一个函数调用。编译器将尝试从使用点(main 内部)进行常规查找,并将移出到封闭的命名空间。 using 指令将在到达根命名空间(::std 和 ::main 的共同祖先)时将 ::std 添加到查找搜索中。此时,由于标识符解析为函数,ADL 将启动。

ADL 将命名空间 关联 添加到函数调用的参数中,在本例中为 boost::(int 等基本类型没有关联的命名空间)。此时编译器看到tie的两个声明:std::tie和boost::tie,造成歧义。

正如您已经知道的那样,解决方案是将呼叫限定为std::tie(我建议您即使没有此问题也可以使用)。关于评论:

如果 ADL 使其解析为 boost::tie for...“我的方便”,然后编译失败,这不应该是编译器选择错误函数的线索吗?!

我不知道您得到的确切错误是什么(我不使用 boost,而且我不知道它包含的 std::tie 可能有哪些重载)。如果问题确实是歧义之一,则问题在于编译器无法解析标识符,并且无法继续该过程。此时它停止并要求程序员解决它。如果错误是它唯一地选择了 boost::tie 并且它后来在分配中失败,则意味着存在 boost::tie 的重载,它比 std::tie 更好的匹配并且被选中。稍后,来自std::tuple 的赋值可能失败,但编译器无法知道问题是在查找期间还是在赋值本身(您是否打算分配该变量?也许是另一个变量?)所以再次它失败并告诉您问题所在。

请注意,通常编译过程总是向前,编译器不会回溯以重复猜测自己的决定*。有一套规则,这些规则适用于每一步。如果有歧义,编译停止,如果没有,则只有一个 best 候选,这一点得到解决,进入下一个。尝试返回撤消决策会使编译过程变得非常缓慢(可以采用的路径数量将呈指数级增长)。

* 与往常一样,有一些例外,但这些只是例外,特别是在重载决议期间,如果一个模板被选为最佳候选但类型参数的替换失败,它会被丢弃并成为下一个最佳模板候选人被选中。

【讨论】:

  • 你是对的,我得到的错误(遗憾的是,它们不够简洁,无法发布,我喜欢 C++,除了与模板相关的这些错误......)是关于 tuple 分配。所以tie 解析为boost::tuples::tie,这会产生一个boost::tuples::tuple 左值,operator = 在其上运行,std::tuple 作为右值,这会产生错误。
  • Re: 编译只向前推进:据我所知,ADL 的目的是为 std::string 加法等运算符提供简洁的语法。奇怪的是,在这种情况下,ADL 要求的语法不太简洁。我仍然认为编译器可能会更聪明一点:它需要做的就是意识到此时正确的tie 名称解析还应该允许应用operator =(const std::tuple&amp;) 之类的东西。自从我为课程项目设计模拟编译器已经很多年了,也许 gcc 比那个更复杂:D
  • @mmd:ADL 的目的是不使代码更简洁,它旨在允许查找以查找运算符(您无法 为在不同命名空间中定义的类型提供一般限定)。您可以(ab)使用它来避免限定,但这是委员会将 Andrew 的方法扩展到 所有 函数,而不仅仅是运算符(这是最初的意图)这一事实的副作用。您无法限定运算符的原因是限定调用的语法取决于运算符的定义方式:ns::operator+(a,b) 与 a.operator+(b)
  • ...当然,设计标准的一部分是允许仅作为运算符使用,并且 是 使代码更简洁 a+b 比任何需要资格的替代方案。
猜你喜欢
  • 2016-07-02
  • 1970-01-01
  • 1970-01-01
  • 2013-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多