Replacement for removed bind1st in C++17
Just use std::bind:
std::bind(std::mem_fn(&CGuild::LoadGuildData), this, std::placeholders::_1)
And you can remove the superfluous std::mem_fn
too:
std::bind(&CGuild::LoadGuildData, this, std::placeholders::_1)
With lambda, you might replace
std::bind1st(std::mem_fn(&CGuild::LoadGuildData), this)
by
[this](auto&& data) {return this->LoadGuildData(data);}
which give at the end something like:
DBManager::Instance().FuncQuery(
[this](auto&& data) {return this->LoadGuildData(data);},
"SELECT master, level, exp, name, skill_point, skill, sp, "
"ladder_point, win, draw, loss, gold FROM guild WHERE id = %u",
m_data.guild_id);
There are two things you could do here. And I find it really unfortunate that the current state of affairs is so awkward.
The most direct substitution is to take your bind1st
and convert it exactly to bind
:
std::bind(&CGuild::LoadGuildData, this, std::placeholders::_1)
Or, if you use bind
semi-regularly, you probably will bring in the placeholders via using
in some form or other, so this becomes:
std::bind(&CGuild::LoadGuildData, this, _1)
This is actually strictly better than bind1st
since this forwards its argument, but bind1st
would not.
The other thing we could do is a lambda. And here, it depends on what LoadGuildData
does exactly. If it returns an object and you don't care about how this bound callable gets used necessarily, you can just write:
[this](auto const& arg){ return LoadGuildData(arg); }
This will work, most of the time. But it's not exactly the same thing as the bind expression. If LoadGuildData()
returned something like an int&
, the bind expression would return an int&
, but this version returns an int
. That might not be important. It might be not be. But if it is, you have to add, at least:
[this](auto const& arg) -> decltype(auto) { return LoadGuildData(arg); }
This will either return a reference type or not a reference type depending on what LoadGuildData
actually returns.
Now... the argument, arg
, might be required to be a modifiable reference, which necessitates
[this](auto&& arg) -> decltype(auto) { return LoadGuildData(arg); }
But sufficiently often, you might need something more. You might need to use this callable in a context that needs to check if it's callable. Right now, due to the rules of how this checking works - all the lambdas I've written will claim that they're callable with any argument at all. Regardless of what LoadGuildData
actually takes. But if it's the wrong type, you'll get a hard compile error. Unfortunate.
So what you really need to do, to write a lambda that has the same behavior as the bind expression I wrote earlier, is:
[this](auto&& arg) -> decltype(LoadGuildData(std::forward<decltype(arg)>(arg))) {
return LoadGuildData(std::forward<decltype(arg)>(arg)));
}
Actually, it's not quite the same behavior. This lambda is actually better in some ways - because the bind expression would not have worked if LoadGuildData
were a member function template or an overload set or took a default argument - but the lambda works in all of these cases. Which is why it is so often recommend to use lambdas - they always work, they're usually the best solution, and they're sometimes the only solution.
But it's a huge mouthful, which is why so many code bases use macros. Like BOOST_HOF_RETURNS
:
#define FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
[this](auto&& arg) BOOST_HOF_RETURNS(LoadGuildData(FWD(arg)))
All of which is to say... we can't have nice things.