(This question popped to my mind after reading this one and its accepted answer.)
Assume a class Foo that you cannot modify, that has a public member bar, but no getter for it.
You might want to write a function to get that memeber when passed a Foo, so that you can use it in higher order functions, such as std::transform and similar.
In other words, given a class like this
struct Foo {
int bar{};
~Foo() { bar = -1; } // on most compilers this helps "verifying" that the object is dead
};
I would like some getFoo such that getBar(expr-of-type-Foo) has the same semantics as expr-of-type-Foo.bar, where expr-of-type-Foo is any object of type Foo, which can be a prvalue, an xvalue, or an lvalue.
How do you write it?
What I initially came up with is:
constexpr auto getBar = overload(
[](auto& foo) -> decltype(auto) { return (foo.bar); },
[](auto const& foo) -> decltype(auto) { return (foo.bar); },
[](auto&& foo) -> auto { return foo.bar; }
);
(-> auto is redundant, but I think it's informative in this context.)
My reasoning was as follows.
I need this getBar to behave differently for the different value categories, so it's a good idea to write it as an overload set; for this purpose I'm using boost::hana::overload.
However I'm not sure the solution I found it's minimal (assuming it is sufficient).
For instance,
Given one of the overloads given I've overloaded on
auto&/auto const&/auto&&, the latter will only catch rvalues, so in the other two cases I know I want to return a reference, so I could return-> auto&and-> auto const&instead ofdecltype(auto)and even remove the parenthesis from thereturned expression:constexpr auto getBar = overload( [](auto& foo) -> auto& { return foo.bar; }, [](auto const& foo) -> auto const& { return foo.bar; }, [](auto&& foo) -> auto { return foo.bar; } );At this, point, though, I don't think I need two overloads
(auto&) -> auto&/(auto const&) -> auto const&, because the former will naturally resolve to the latter when fed with aconstexpression, so I think I could go for this:constexpr auto getBar = overload( [](auto& foo) -> auto& { return foo.bar; }, [](auto&& foo) -> auto { return foo.bar; } );
But I don't see a way to simplify it further, at the moment.
Here are my attempts to test it:
#include<assert.h>
#include<boost/hana/functional/overload.hpp>
struct Foo {
int bar{};
~Foo() {
bar = -1;
}
};
int main() {
{
constexpr auto getBar = overload(
[](auto&& foo) -> auto { return foo.bar; },
[](auto& foo) -> auto& { return foo.bar; }
);
{
Foo foo{3};
assert(&getBar(foo) == &foo.bar);
assert(getBar(foo) == 3);
foo.bar = 4;
assert(foo.bar == 4);
assert(getBar(foo) == 4);
getBar(foo) = 5;
assert(foo.bar == 5);
assert(getBar(foo) == 5);
}
{
Foo const foo{3};
assert(&getBar(foo) == &foo.bar);
assert(getBar(foo) == 3);
//foo.bar = 3; // Expectedly fails to compile.
//getBar(foo) = 3; // Expectedly fails to compile.
}
{
auto const& foobar = getBar(Foo{3});
assert(foobar == 3);
//foobar = 5; // Expectedly fails to compile.
}
{
//auto& foobar = getBar(Foo{3}); // Expectedly fails to compile.
//auto& foobar = Foo{3}.bar; // Expectedly fails to compile.
}
}
}
Well, for minimal work:
This particular use case appears to do what you want. In C++20, it was upgraded to be
constexpr, but that might or might not be available. Unlike the other question, which has overloadable member functions, a member variable isn't ambiguous. So I would fall back to the above.