[PATCH] D99560: Utility to construct visitors from lambdas.

Scott Linder via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 14 13:32:47 PDT 2021


scott.linder added a comment.

I think I ran into a reason to require the visitor we create be able to derive from all of its `Callable`s: it makes it possible to transparently select the correct overload via the normal overload resolution rules, which as of C++14 and the addition of generic lambdas gives the user a way to naturally describe a "default" case.

For example, with the inheritance version this works:

  variant<bool, int, double> V;
  visit(makeVisitor(
    [](int Int) { /* ... */ },
    [](auto BoolOrDouble) { /* ... */ }
  ), V);

And it is equivalent to this:

  variant<bool, int, double> V;
  visit(makeVisitor(
    [](auto BoolOrDouble) { /* ... */ },
    [](int Int) { /* ... */ },
  ), V);

This is possible because inheritance permits `using Callable::operator();` to actually bring all of the overloads into one scope, and the generic lambda de-sugars to a template lambda, which is always ordered after any other non-template lambdas during overload resolution.

Do we know any way to achieve this without inheritance? The closest approximation I could come up with is to make the order of the arguments to `makeVisitor` significant, such that they are visited "in order" until one is invokable. This means that the following always selects the generic lambda during overload resolution, and the other lambda is dead code:

  variant<bool, int, double> V;
  visit(makeVisitor(
    [](auto IntOrBoolOrDouble) { /* ... */ },
    [](int Int) { llvm_unreachable(); },
  ), V);

This seems a little too surprising to be acceptable, but I suppose either version could be fine so long as we are explicitly about documenting the behavior?

It may just come down to what non-derivable types we expect the user to be interested in using here? I suspect the most common case will be free functions, which I think we can handle by wrapping them in `function_ref`.

https://godbolt.org/z/9GW3n6nYh is a version using inheritance, but perfectly forwarding the arguments to the appropriate constructor overload. Once we get to C++20 I think passing a prvalue lambda expression will be guaranteed to never call a copy or move constructor, and before then clang seems perfectly capable of eliding them.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D99560/new/

https://reviews.llvm.org/D99560



More information about the llvm-commits mailing list