g++ std::visit 泄漏到全局命名空间?

g++ std::visit leaking into global namespace?

我刚刚在 std::visit 和 std::function 附近遇到了一些微妙的事情,这让我很困惑。我并不孤单,但我能找到的唯一其他人跳了 "workaround and move on" 舞,这对我来说还不够:

这可能与 LWG 中的一个未解决问题有关,但我认为这里发生了更险恶的事情:

最小示例:

// workaround 1: don't include <variant>
#include <variant>
#include <functional>

struct Target
{
  Target *next = nullptr;
};

struct Visitor
{
  void operator()(const Target &tt) const { }
};

// workaround 2: concretely use 'const Visitor &' instead of 'std::function<...>'
void visit(const Target &target, const std::function<void(const Target &)> &visitor)
{
  visitor(target);
  if(target.next)
    visit(*target.next,visitor); // workaround 3: explicitly invoke ::visit(...)
    //^^^ problem: compiler is trying to resolve this as std::visit(...)
}

int main(int argc, char **argv, char **envp)
{
  return 0;
}

使用 g++ -std=c++17 编译,测试使用:

最终结果是编译器尝试使用 std::visit 来调用 clearly-not-std visit(*target.next,visitor):

g++-8 -std=c++17 -o wtvariant wtvariant.cpp
In file included from sneakyvisitor.cpp:3:
/usr/include/c++/8/variant: In instantiation of ‘constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = Target&; _Variants = {const std::function<void(const Target&)>&}]’:
wtvariant.cpp:20:31:   required from here
/usr/include/c++/8/variant:1385:23: error: ‘const class std::function<void(const Target&)>’ has no member named ‘valueless_by_exception’
       if ((__variants.valueless_by_exception() || ...))
            ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/8/variant:1390:17: error: no matching function for call to ‘get<0>(const std::function<void(const Target&)>&)’
      std::get<0>(std::forward<_Variants>(__variants))...));
      ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

在我的真实用例中,我以为有人偷偷把 "using namespace std" 偷偷塞进了我的树的 header space 中,我会很生气。然而,这个最小的例子证明了不同。

关键问题:鉴于我没有创建或使用任何名称space,为什么 std::visit(...) 会参与其中?

三个标记的解决方法中的任何一个都会抑制编译器错误,但我个人不能容忍我无法解决的语言故障(虽然我理解必要性,但我仍然对如何解决感到不安通常我必须在模板中添加 'typename' 才能编译它们。

另外值得注意的是,如果尝试在没有限定的情况下使用 std 名称的其他元素space(例如,尝试裸 'cout'),编译器会适当地抱怨无法弄清楚我所追求的 'cout',所以变体 header 并不是以某种方式压扁标准名称 space.

最后,即使我将 visit() 方法放在它自己的名称中,这个问题仍然存在space:编译器真的想使用 std::visit(...) 除非我显式调用 my_namespace::visit(...).

我错过了什么?

参数 visitor 是一个 std::function,它在命名空间 std 中,因此 argument-dependent lookup 在命名空间 [=12= 中找到 visit ] 还有。

如果您总是希望在全局命名空间中使用 visit,请使用 ::visit