I just bounced into something subtle in the vicinity of std::visit and std::function that baffles me. I'm not alone, but the only other folks I could find did the "workaround and move on" dance, and that's not enough for me:
This may be related to an open issue in the LWG, but I think something more sinister is happening here:
Minimal Example:
// 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;
}
Compile with g++ -std=c++17, tested using:
The net result is the compiler tries to use std::visit for the clearly-not-std invocation of 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))...));
~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In my real use case, I thought someone had snuck a "using namespace std" into the header space of my tree and I was gonna be grumpy. However, this minimal example demonstrates otherwise.
Critical Question: given that I have not created nor used any namespaces, why is std::visit(...) getting involved here at all?
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?
The argument visitor
is an std::function
, which is in the namespace std
, so argument-dependent lookup finds visit
in the namespace std
as well.
If you always want the visit
in the global namespace, say so with ::visit
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With