我刚刚在 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 编译,测试使用:
最终的结果是编译器试图使用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(...)会参与其中?
三种标记的变通方法中的任何一种都会抑制编译器错误,但我个人无法容忍我无法理解的语言小毛病(虽然我理解必要性,但我仍然对经常要在模板中撒入 "typename "以使其编译感到不安)。
同样值得注意的是,如果尝试使用std命名空间的其他元素而不加任何限定(例如,尝试一个裸露的'cout'),编译器就会因为无法找出我想要的'cout'而大发牢骚,所以这并不是说变体头以某种方式扁平化了std命名空间。
最后,即使我把我的 visit() 方法放在它自己的命名空间中,这个问题依然存在:编译器真的想使用 std::visit(...) ,除非我明确地调用 my_namespace::visit(...) 。
我错过了什么?
参数 visitor
是一个 std::function
命名空间中的 std
所以 依赖于参数的查询 发现 visit
命名空间中的 std
也是。
如果你总是想把 visit
在全局命名空间中,用 ::visit
.