(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 of decltype(auto)
and even remove the parenthesis from the return
ed 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 a const
expression, 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:
const auto getBar = std::mem_fn(&Foo::bar);
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.