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

Chr*_*ker 8 c++ g++ visitor variant c++17

我只是在 std::visit 和 std::function 附近反弹到一些微妙的东西,这让我感到困惑。我并不孤单,但我能找到的唯一其他人做了“解决方法并继续前进”的舞蹈,这对我来说还不够:

这可能与 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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • g++-7 (Ubuntu 7.5.0-3ubuntu1~18.04)
  • g++-8 (Ubuntu 8.4.0-1ubuntu1~18.04)

最终结果是编译器尝试使用 std::visit 来调用显然不是 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))...));
      ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

在我的实际用例中,我认为有人在我的树的标题空间中偷偷地添加了一个“使用命名空间 std”,我会变得脾气暴躁。然而,这个最小的例子展示了另外的情况。

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

  • WRT 解决方法 1:至少在变体标头中,visit(...) 在 std 命名空间中正确声明
  • WRT workaround 2: If the second argument is anything other than a std::function, it compiles just fine, leading me to believe that something more subtle is going on here.
  • WRT workaround 3: I understand that two colons are a small price to pay, but that they are necessary at all feels dangerous to me given my expectations for what it means to put a free function like visit(...) into a namespace.

Any one of the three marked workarounds will suppress the compiler error, but I'm personally intolerant of language glitches that I can't wrap my head around (Though I understand the necessity, I'm still uneasy about how often I have to sprinkle 'typename' into templates to make them compile).

Also of note, if try to make use of other elements of the std namespace without qualification (e.g., try a naked 'cout'), the compiler properly grumps about not being able to figure out the 'cout' that I'm after, so it's not as though the variant header is somehow flattening the std namespace.

Lastly, this problem persists even if I put my visit() method in its own namespace: the compiler really wants to use std::visit(...) unless I explicitly invoke my_namespace::visit(...).

What am I missing?

Ast*_*ngs 4

参数visitor是 an std::function,它位于命名空间中std,因此依赖于参数的查找也会visit在命名空间中找到。std

如果您始终希望visit在全局命名空间中使用 ,请使用 来表示::visit