使用命名空间 std 导致 boost 指针转换触发 C++17 标准中的 ADL

kla*_*aus 20 c++ boost argument-dependent-lookup c++17 c++20

我有一个带有继承和shared_ptr来自 boost 库的简单代码。使用标准 c++20,代码可以正常编译。函数调用static_pointer_castdynamic_pointer_cast编译无需预先boost::命名空间——这些函数调用之所以有效,是因为 ADL(参数相关查找)。

但对于标准 c++17,代码将无法编译。我认为 ADL 没有实现,或者实现方式不同。但是,如果我添加using namespace std,代码就可以正常编译。我的问题是:与库的函数调用std有什么关系?boost

这是在线编译器,因此您可以通过注释行中和注释行来进行操作using namespace std;: https: //godbolt.org/z/cz8Md5Ezf

#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

// using namespace std;

class Animal {
public:
  Animal()
  {}
  virtual ~Animal()
  {}
  void speak()
  {
    std::cout << "I am an animal\n";
  }
};

class Dog : public Animal {
public:
  Dog()
  {}
  void bark()
  {
    std::cout << "Gheu --> ";
  }
  void speak()
  {
    bark();
    Animal::speak();
  }
};

typedef boost::shared_ptr<Animal> AnimalPtr;
typedef boost::shared_ptr<Dog> DogPtr;

int main()
{
  AnimalPtr ap = boost::make_shared<Animal>();
  DogPtr dp = boost::make_shared<Dog>();

  AnimalPtr ap1 = static_pointer_cast<Animal>(dp);
  DogPtr dp1 = dynamic_pointer_cast<Dog>(ap1);

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果您在想,“好吧,如果使用命名空间 std 可以消除错误,那么这些指针转换调用必须来自 std 库。” 我也是这么想。但是通过gdb查看,我发现不,with using namespace std,这些函数肯定是从boost库调用的。

在此输入图像描述

use*_*522 22

当编译器<在表达式中的标识符后面看到 a 时(例如 in static_pointer_cast<Animal>(dp)),它需要确定它是否<引用关系型 less 运算符,或者它是否是模板参数列表的开头。

如果通过通常的非限定名称查找(不是 ADL)找到模板,则假定是后者。但是如果没有找到模板怎么办?

在 C++20 之前,如果查找没有找到模板,则假定该名称不引用模板,因此以下<是 less 运算符。

在你原来的情况下,这是没有意义的。例如,没有任何东西static_pointer_cast可以作为操作数来引用<。因此,根据编译器的不同,它会失败并显示或多或少清晰的错误消息。

编译器using namespace std;可能会隐式包含std::static_pointer_cast(来自<memory>)您显式包含的标头之一(无法保证),或者 boost 标头可能包含<memory>. 如果发生这种情况,那么非限定查找static_pointer_cast将找到它,并且因为它是一个模板,<所以它会被视为启动模板参数列表而不是关系运算符。那么很明显,整个表达式是一个带有模板参数列表的非限定函数调用。

从 C++20 开始,如果查找没有找到任何内容或根本没有找到任何函数,则还假定<名称后面的 a 引入了模板参数列表。

通过解释为模板参数列表和函数调用,将完成正常的非限定名称查找和非限定函数调用的 ADL,这会找到两者boost::shared_pointer_cast(通过 ADL)并且std::shared_pointer_cast如果您正在使用using namespace std;,但因为您正在传递boost::shared_ptr版本std,这需要参数std::shared_ptr,将不可行,并且boost版本将始终由重载决议选择。这部分在 C++20 中没有变化。

请注意,这是 C++20 中的重大更改。在某些不寻常的情况下,您可能希望<在函数名称之后实际引用关系运算符,而现在需要将函数名称加上括号。有关示例,请参阅[diff.cpp17.temp] 。


归档时间:

查看次数:

996 次

最近记录:

3 年,2 月 前