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

问题描述 投票:1回答:1

我刚刚在 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;
}

用 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))...));
      ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

在我的实际使用案例中,我以为有人在我的树的头部空间里偷偷地写了 "使用命名空间std",然后我就会很生气。 然而,这个最小的例子却证明了另一种情况。

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

  • WRT workaround 1: 至少在变体头中,visit(...)被正确地声明在std命名空间中。
  • WRT workaround 2: 如果第二个参数不是 std::function,那么它的编译就会很好,这让我相信这里发生了一些更微妙的事情。
  • WRT workaround 3: 我知道两个冒号的代价不大,但考虑到我对将一个像 visit(...)这样的自由函数放到命名空间中的期望,我觉得这两个冒号很危险。

三种标记的变通方法中的任何一种都会抑制编译器错误,但我个人无法容忍我无法理解的语言小毛病(虽然我理解必要性,但我仍然对经常要在模板中撒入 "typename "以使其编译感到不安)。

同样值得注意的是,如果尝试使用std命名空间的其他元素而不加任何限定(例如,尝试一个裸露的'cout'),编译器就会因为无法找出我想要的'cout'而大发牢骚,所以这并不是说变体头以某种方式扁平化了std命名空间。

最后,即使我把我的 visit() 方法放在它自己的命名空间中,这个问题依然存在:编译器真的想使用 std::visit(...) ,除非我明确地调用 my_namespace::visit(...) 。

我错过了什么?

c++ g++ c++17 variant visitor
1个回答
4
投票

参数 visitor 是一个 std::function命名空间中的 std所以 依赖于参数的查询 发现 visit 命名空间中的 std 也是。

如果你总是想把 visit 在全局命名空间中,用 ::visit.

© www.soinside.com 2019 - 2024. All rights reserved.