0

At the doc page of boost::hana::always I read that

always(x) is a function such that

always(x)(y...) == x

for any y....

This makes me think that it shouldn't behave any differently than this lambda: [](auto const&...){ return false; }.

However it does. For instance, the following code prints 11, but if I change the third lambda to hana::always(false), then it prints 00, revealing that always is swallowing any argument.

#include <boost/hana/functional/always.hpp>
#include <boost/hana/functional/overload.hpp>
#include <iostream>

auto fun = boost::hana::overload(
        [](int){ return true; },
        [](double){ return true; },
        [](auto const&...){ return false; }
        );

int main() {
    std::cout << fun(1) << fun(1.0) << std::endl;
}
  • Is this expected?
  • If so, why?
  • Whether or not is expected, what causes this behavior?

By the way, I've just discovered boost::hana::overload_linearly, which is not an alternative to boost::hana::overload in this case (because as much as always would not get all the calls, it would be the [](int){ return true; } to be greedy), but it's good to know of it.

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • 1
    With `mutable`, "`[](auto const&...) mutable`" would be a better match than `[](int)` as `fun` is not `const`. – Jarod42 Mar 16 '21 at 12:04
  • Yeah, `always` has three overloads for const and reference categories. – Jason Rice Mar 16 '21 at 18:05
  • @Jarod42, would you elaborate in an answer? – Enlico Mar 17 '21 at 15:16
  • Related to [c-overload-pattern-call-resolution-with-mutable-lambda](https://stackoverflow.com/questions/66890356/c-overload-pattern-call-resolution-with-mutable-lambda) – Jarod42 Mar 31 '21 at 15:36
  • @Jarod42, I would say _that_ is related to _this_ one, based on seniority, ahahah. – Enlico Mar 31 '21 at 16:40
  • This one is more specific with boost (and the fact that you didn't identify the "`mutable`" overload). That's why I link in that order. – Jarod42 Apr 01 '21 at 07:15

1 Answers1

2

In fact, always has different overloads (as it handles reference as return value).

so, with a simplified version:

template <typename T>
struct my_always
{
     T res;

     template <typename ...&& Ts>
     T& operator ()(Ts&&...) /* mutable */ { return res; } // #1

     template <typename ...&& Ts>
     const T& operator ()(Ts&&...) const { return res; } // #2
};

then

auto fun = boost::hana::overload(
        my_always<bool>{ false }, // #1 & #2
        [](int){ return true; }   // #3
        );

std::cout << fun(1);

Possible overloads are:

  • bool& my_always<bool>::operator ()(int&&) #1
  • const bool& my_always<bool>::operator ()(int&&) const #2
  • bool lambda::operator() (int) const #3

All are viable here, but #1 is the best match (as fun is not const (and int is not better than int&&)).

With const fun, #3 would be the best match (#1 not viable, tie-breaker between #2 and #3 is template status).

Enlico
  • 23,259
  • 6
  • 48
  • 102
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I still have some misunderstanding. You write _All are viable here, but #1 is a better match (as `fun` is not `const`_; I'd say that it's the only non-`const` among the 3 so why do you specify that _`int` is not better than `int&&`_? I think I'm missing something here. Furthermore, would you spend another word on how adding `const` to the function object `fun` interacts with the `const` of the `operator()` of the various arguments to `hana::overload`? – Enlico Mar 18 '21 at 09:05
  • overload resolution has complex rules to be "intuitive", we have to check all parameters to know which function is the best. [Demo](http://coliru.stacked-crooked.com/a/03c18e19e469d9ea) showing ambiguous when constnes is ok, but parameter requires conversion (`void S::print(float) const` versus `void print(int)` for `s.print(4.2f);` one is better for `float` parameter, the other is better for non const `S`, neither is better -> ambiguous call). – Jarod42 Mar 18 '21 at 09:23
  • Maybe turning method to free functions (so adding hidden `this` as parameter) might help to see the ambiguity `void f(S&, const S&); void f(const S&, S&); S s; f(s, s); /*Ambiguous*/`. – Jarod42 Mar 18 '21 at 09:27
  • Thinking again about it. So the point is that when overloading non-mutable lambdas, the overload resolution is effectively only on their arguments, because they're all uniform with respect to the `this` argument; i.e. wheteher `foo` is `const` or not, the implication in terms of how good the match is is the same on all of them, so the net effect is none. But `always` introduces both `const` and non-`const` `operator()`s, so the goodness of its match in overload resolution has one more player, the `this`. – Enlico Nov 18 '22 at 16:45