perfect forwarding and order of function arguments evaluation - c++

Is there a good way to forward arguments of a function f to a function g in a situation like
template<typename... T>
void g(T && ... x);
template<typename... T>
void f(T && ... x)
{
g(x..., x...);
}
In the next code x could be moved twice
template<typename... T>
void f(T && ... x)
{
g(std::forward<T>(x)..., std::forward<T>(x)...);
}
In the next code std::forward<T>(x)... could be evaluated before x...
template<typename... T>
void f(T && ... x)
{
g(x..., std::forward<T>(x)...);
}

std::forward doesn't move things - it creates a reference that says "it is ok to move from me". The actual moving occurs inside g, not in f where std::forward or std::move is called.
The problem of move is only one of the problems here. There is also the problem of passing the same object twice as a reference in two spots, which is generally considered quite rude!
We can fix that by creating temporary objects in f and pass those by reference, but that leads to a serious issue: references are often used to return values from a function, and we have the same variable being used twice -- we cannot return both results.
So the answer is "don't do that", because it is not in general safe. You have to know the semantics of both g and f to figure out what the correct thing to do is, and a simple forwarding type interface won't reflect the depth of knowledge required.
If you do have deep semantic understanding of what g and f are supposed to do, then the situation changes.

You can in general force the order using
using separate statements (obviously)
expressions separated by the comma operator. (Beware of overloaded operator,)
The use of brace initialization works because the order of evaluation of the arguments in a brace initializer list is the order in which they appear1. The following has well-defined evaluation order:
std::tuple<T..., T...> args {
std::forward<T>(x)...,
std::forward<T>(x)... }; // still not sane, but evaluation order defined
But it's still useless as g(...) might still move from the same reference twice. What you'd actually want for rvalue refs is not:
g(rvalue, std::move(rvalue)); // or
g(std::move(rvalue), rvalue); // or even
g(std::move(rvalue), std::move(rvalue)); // [sic]
The only sane way would be:
g(lvalue=std::move(rvalue), lvalue); // BUT: fix the evaluation order
So how do we achieve precisely that but generically?
Enter Indices?!
Let's say you have variadic g as you described:
template<typename... T>
void g(T && ... x)
{
}
Now, you can duplicate the arguments passed to f using
the index trick:
namespace detail // indices
{
template<std::size_t... Is> struct seq{};
template<std::size_t I, std::size_t... Is>
struct gen_seq : gen_seq<I-1, I-1, Is...>{};
template<std::size_t... Is>
struct gen_seq<0, Is...>{ using type = seq<Is...>; };
}
and an invoker helper function:
#include <tuple>
template<typename Tuple, std::size_t... Is>
void f_invoke_helper(Tuple const& tup, detail::seq<Is...>)
{
g(std::get<Is>(tup)..., std::get<Is>(tup)...);
}
All that's required next is to tie it all together:
template<typename... T>
void f(T && ... x)
{
f_invoke_helper(
std::make_tuple(std::forward<T>(x)...),
typename detail::gen_seq<sizeof...(T)>::type());
}
Note that if you pass rvalue-refs, it will get moved once (into the tuple) and used twice (as a lvalue) in the invoker helper:
int main()
{
std::string x = "Hello world";
int i = 42;
// no problem:
f(i, -42, std::move(x));
}
Hope this helps!
PS. As it has been aptly pointed out, it's probably a lot easier to just say
template<typename... T>
void f(T&&... x) { g(x..., x...); }
I haven't thought of a way in which the tuple idiom doesn't result in the same, except for actually moving movable arguments into the tuple.
1The semantics of T{...} are described in 12.6.1See also: how to avoid undefined execution order for the constructors when using std::make_tuple .

Related

Iterating over Variadic arguments

I'm sure this question has been asked before, but I can't seem to find something as simple as what I'm trying to do. Essentially, I just want to make sure the code triggers each parameter, calling any functions that may be sent as inputs. My biggest worry here is that optimizations may remove some of the calls, changing the result.
I was using the following syntax. It seemed to trigger the function calls the way I want, but it has the strange result of triggering the arguments in reverse order - the last argument is called first, and the first argument is called last:
template <typename... PARMS> uint PARMS_COUNT(PARMS&& ... parms) { return static_cast<uint>( sizeof...(parms) ); }
This was my first guess as to how to do the same thing (edit: this does not change the order - it still happens in reverse, because the order is being determined by the function parameter evaluation, rather than what order the function uses them in):
template <typename FIRST>
constexpr uint PARMS_EXPAND(FIRST &&first)
{
return static_cast<uint>( sizeof(first) > 0 ? 1 : 0 );
}
template <typename FIRST,typename... PARMS>
constexpr uint PARMS_EXPAND(FIRST &&first,PARMS&& ... parms)
{
return static_cast<uint>( sizeof(first) > 0 ? 1 : 0 ) + PARMS_EXPAND( std::forward<PARMS>(parms)... );
}
I tested this in a few places, but then realized that regardless of how much testing I do, I'll never know if this is a safe way to do it. Is there a standard or well known method to pull this logic off? Or even better, some built in system to iterate over the arguments and "access them" in the correct order?
To better explain why I would want to trigger code like this, consider a function that can add new objects to a parent:
void AddObject(/*SINGLE UNKNOWN INPUT*/)
{
...
}
template <typename... PARMS> AddObjects(PARMS&& ... parms)
{
PARAMS_EXPAND( AddObject(parms)... );
}
When you write
void AddObject(T);
template <typename... PARMS> AddObjects(PARMS&& ... parms)
{
PARAMS_EXPAND( AddObject(parms)... );
}
you're making a single top-level function call to PARAMS_EXPAND with sizeof...(PARMS) arguments.
What happens inside PARAMS_EXPAND is essentially irrelevant because, like every other function call, the arguments are evaluated before the function is entered. And, like every other function call, the argument expressions are intederminately sequenced with respect to each other.
So, your goal of evaluating AddObject(parms) for each parameter in order has failed before control reaches inside PARAMS_EXPAND at all.
Fortunately, C++17 gave us a way of evaluating operations on parameter packs in order without relying on function call semantics: the fold expression:
(AddObject(parms), ...);
Although this looks a bit like a function call, it's actually a right fold over the comma operator, expanded like
(AddObject(p0) , (... , (AddObject(pN-1) , AddObject(PN))));
which does indeed evaluate its arguments in left-to-right order, exactly as you want.
I answer the original question because it's not clear what you're asking for (see comment) later. If you want n arguments to be called in order, i.e. call operator() on them (e.g. for lambdas, functors), you can do:
constexpr void callEach() {}
template<typename F, typename... Fs>
constexpr void callEach(F f, Fs&&... fs)
{
f();
callEach(std::forward<Fs>(fs)...);
}
Any way that doesn't take lambdas cannot guarantee evaluation order.
In this line:
PARAMS_EXPAND( AddObject(parms)... );
the order of evaluation is not specified because there is no sequence point between each item. Also I'm confused as to why PARAMS_EXPAND does anything if you are going to discard the result. It might as well be this:
template <typename... PARMS>
void PARAMS_EXPAND(PARMS&&...) {}
If you just want to call AddObject with each parms in order, then you need to pass the function and the parameters separately:
void print(int i) {
std::cout << i << " ";
}
template <typename F, typename Head>
constexpr void call_for_each(F&& f, Head&& head)
{
f(head);
}
template <typename F, typename Head, typename... Tail>
constexpr void call_for_each(F&& f, Head&& head, Tail&&... tail)
{
f(std::forward<Head>(head));
call_for_each(std::forward<F>(f), std::forward<Tail>(tail)...);
}
template <typename... Args>
void print_all(Args&&... args)
{
call_for_each(print, std::forward<Args>(args)...);
}
Demo
Here the calls are correctly sequenced: first call f with head, then make the recursive call for the rest of the arguments in tail.
In C++17, you can use a fold expression instead:
template <typename F, typename... Args>
constexpr void call_for_each(F&& f, Args&&... args)
{
(f(std::forward<Args>(args)), ...);
}
Demo
This basically generates a line like so:
(f(std::forward<Args0>(args0), f(std::forward<Args1>(args1), ..., f(std::forward<ArgsN>(argsN));
where ArgsI and argsI are the I-th element of Args and args in the correct order.
Here, the calls are again correctly sequenced because of the comma operator.

How to correctly forward and use a nested tuple of constexpr struct with standard tuple operations

I want to store passed data via constexpr constructor of a struct, and store the data in a std::tuple, to perform various TMP / compile time operations.
Implementation
template <typename... _Ts>
struct myInitializer {
std::tuple<_Ts...> init_data;
constexpr myInitializer(_Ts&&... _Vs)
: init_data{ std::tuple(std::forward<_Ts>(_Vs)...) }
{}
};
Stored data uses a lightweight strong type struct, generated via lvalue and rvalue helper overload:
template <typename T, typename... Ts>
struct data_of_t {
using type = T;
using data_t = std::tuple<Ts...>;
data_t data;
constexpr data_of_t(Ts&&... _vs)
: data(std::forward<Ts>(_vs)...)
{}
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
template<typename T, typename... Ts>
constexpr auto data_of(Ts&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
It's implemented like
template <typename T = int>
class test {
public:
static constexpr auto func(int p0=0, int p1=1, int p2=3) noexcept {
return data_of <test<T>>
(data_of<test<T>>(p0, p1));
}
};
int main() {
constexpr // fails to run constexpr // works without
auto init = myInitializer (
test<int>::func()
,test<int>::func(3)
,test<int>::func(4,5)
);
std::apply([&](auto&&... args) {
//std::cout << __PRETTY_FUNCTION__ << std::endl;
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
}
, init.init_data);
}
Getting to the point
std::tuple_cat fails if myInitializer instance is constexpr.
std::apply([&](auto&&... args) {
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
It appears to be related to the const qualifier added via constexpr.
How can this be fixed?
See full example at https://godbolt.org/z/j5xdT39aE
This:
auto merged_tuple = std::tuple_cat(std::forward<decltype(args.data)>(args.data)...);
is not the right way to forward data. decltype(args.data) is going to give you the type of that data member - which is not a function of either the const-ness or value category of args. Let's take a simpler example:
void f(auto&& arg) {
g(std::forward<decltype(arg.data)>(arg.data));
}
struct C { int data; };
C c1{1};
const C c2{2};
f(c1);
f(c2);
f(C{3});
So here I have three calls to f (which call f<C&>, f<const C&>, and f<C>, respectively). In all three cases, decltype(arg.data) is... just int. That's what the type of C::data is. But that's not how it needs to be forwarded (it won't compile for c2 because we're trying to cast away const-ness -- as in your example -- and it'll erroneously move out of c1).
What you want is to forward arg, separately, and then access data:
void f(auto&& arg) {
g(std::forward<decltype(arg)>(arg).data);
}
Now, decltype(arg) actually varies from instantiation to instantiation, which is a good indicator that we're doing something sensible.
In addition of the forwarding problem denoted by Barry, there's a different reason why you cannot have constexpr on init. This is because you contain a reference to a temporary inside data_of_t.
You see, you are containing a type obtained from overload resolution from a forwarding reference:
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, Ts...>(std::forward<Ts>(_vs)...);
};
The Ts... in this case could be something like int, float const&, double&. You send those reference type and then you contain them inside of the std::tuple in data_of_t.
Those temporaries are local variables from the test function:
template <typename T = int>
class test {
public:
static constexpr auto func(int p0=0, int p1=1, int p2=3) noexcept {
return data_of <test<T>>
(data_of<test<T>>(p0, p1));
}
};
The problem here is that p0, p1, p2 are all local variable. You send them in test_of_t which will contain references to them, and you return the object containing all those reference to the local variable. This is maybe the cause of the MSVC crash. Compiler are required to provide diagnostic for any undefined behaviour in constexpr context. This crash is 100% a compiler bug and you should report it.
So how do you fix that?
Simply don't contain references by changing data_of:
template<typename T, typename... Ts>
constexpr auto data_of(Ts&&... _vs) {
return data_of_t<T, std::decay_t<Ts>...>(std::forward<Ts>(_vs)...);
};
This will decay the type thus removing the references and decay any reference to C array to pointers.
Then, you have to change your constructor. You call std::forward in there but it's no forwarding occurring if you decay in the template arguments.
template<typename... Vs> requires((std::same_as<std::decay_t<Vs>, Ts>) && ...)
constexpr data_of_t(Vs... _vs)
: data(std::forward<Vs>(_vs)...)
{}
This will add proper forwarding and also constrain it properly so it always do as data_of intended.
Just doing those change will remove UB from the code, but also change it a bit. The type data_of_t will always contain values, and won't contain references. If you want to send a reference, you will need something like std::ref, just like std::bind have to use to defer parameters.
You will still need to use std::forward<decltype(arg)>(arg).data for proper forwarding as #Barry stated

Use of std::forward for non-forwarding references

I am trying to figure out if my use of std::forward in the following code makes sense even though it is not a forwarding (=universal) reference. Please excuse the amount of code, but this is the stripped-down version of what I am trying to achieve.
template <class... Args>
class event_dispatcher {
private:
using func_t = std::function<bool(Args...)>;
public:
bool operator()(Args... args) const {
for (auto& f : funcs) {
if (!f(args...))
return false;
}
return true;
}
template <class F>
std::enable_if_t<std::is_invocable_r_v<bool, F, Args...>>
add(F&& f) {
funcs.emplace_back(std::forward<F>(f));
}
template <class F>
std::enable_if_t<!std::is_invocable_r_v<bool, F, Args...> && std::is_invocable_v<F, Args...>>
add(F&& f) {
add([f_ = std::forward<F>(f)](Args... args){
std::invoke(f_, std::forward<Args>(args)...); // <-- this is the one I am asking about!
return true;
});
}
private:
std::vector<func_t> funcs;
};
The idea is that if the callable passed to add() doesn't return a bool, we will wrap it in a lambda that does.
Now, if I just pass args... directly, it will always work, but if any of Args are rvalues then it will needlessly do a copy instead of a move. If I instead use std::move(args)... it will not work if any of Args is an lvalue. Using std::forward<Args>(args)... here seems to solve these problems and work as efficiently as possible in any case, but I am worried that I am missing something since I am using std::forward for a non-forwarding reference, and in general I am having a lot of trouble wrapping my head around the whole reference collapsing / rvalue reference / std::move / std::forward issues.
std::move does not move std::forward does not forward.
std::forward is a conditional move. std::forward<T> moves if T is a value or rvalue reference.
This lines up with when you want to move args..., so it is appropriate here.
A comment along those lines should be a good idea, as in any situation where you use std::forward outside of simple forwarding references.

"Empty base optimization" for lambda captures - forbidden by the Standard? Why?

I recently came across a situation where I ended up with a large number of nested lambdas to build asynchronous computation chains.
template <typename F>
struct node : F
{
node(F&& f) : F{std::move(f)}
{
}
template <typename FThen>
auto then(FThen&& f_then)
{
return ::node{[p = std::move(*this), t = std::move(f_then)]()
{
}};
}
};
int main()
{
auto f = node{[]{ }}.then([]{ }).then([]{ });
return sizeof(f);
}
All the objects I capture in my the lambdas are empty, yet the size of the final object is greater than one: example on gcc.godbolt.org.
If I change the lambda inside node</* ... */>::then to a function object with explicit EBO, the size of the final object becomes one.
template <typename P, typename T>
struct node_lambda : P, T
{
node_lambda(P&& p, T&& t) : P{std::move(p)}, T{std::move(t)}
{
}
void operator()()
{
}
};
template <typename FThen>
auto node</* ... */>::then(FThen&& f_then)
{
return ::node{node_lambda{std::move(*this), std::move(f_then)}};
}
Live example on gcc.godbolt.org
I find this really annoying because I'm forced to either:
Write a lot of boilerplate code that is roughly equivalent to the lambda.
Pay an additional memory cost due to the fact that something like EBO doesn't apply to lambda captures.
Is there anything in the Standard that explicitly forces empty lambda captures to take additional space? If so, why?
From expr.prim.lambda.capture:
For each entity captured by copy, an unnamed non-static data member is declared in the closure type.
While the lambdas here have no capture:
auto f = node{[]{ }}.then([]{ }).then([]{ });
and hence have no unnamed non-static data members, and hence are empty, that's not what then() actually uses. It uses this:
return ::node{[p = std::move(*this), t = std::move(f_then)](){}};
that lambda captures t and p by copy, and hence has two unnamed non-static data members. Each .then() adds another member variable, even if each one is empty, hence the size of the node keeps going up.
Or in other words, the empty base optimization only applies to bases, and capture for lambdas doesn't create bases, it creates non-static data members.
The other answers have the cause, so I will not re-iterate. I will just add that I was able to turn your example into an inheritance based one without too much boilerplate. Since you do public inheritance in the OP, I opted to remove the c'tor and go for aggregate initialization.
It only required two deduction guides to make the code almost as pretty as your original scheme:
Live on Coliru
#include <utility>
#include <iostream>
struct empty {
void operator()() {}
};
template <typename P, typename T>
struct node : P, T
{
template <typename FThen>
auto then(FThen&& f_then)
{
return ::node{std::move(*this), std::forward<FThen>(f_then)};
}
void operator()() {
P::operator()();
T::operator()();
}
};
template <typename P> node(P) -> node<P, ::empty>;
template <typename P, typename T> node(P, T) -> node<P, T>;
int main()
{
auto f = node{[]{ }}.then([]{ }).then([]{ });
std::cout << sizeof(f);
}
The EBO was applied, as you can see by following the link.
BTW, since we are are moving *this, it may be worth to r-value qualify node::then. Just to avoid any nastiness.
Given the as-if rule and [expr.prim.lambda.closure]/2:
An implementation may define the closure type differently from what is
described below provided this does not alter the observable behavior
of the program other than by changing:
the size and/or alignment of the closure type,
whether the closure type is trivially copyable (Clause [class]),
whether the closure type is a standard-layout class (Clause [class]), or
whether the closure type is a POD class (Clause [class]).
I don't see anything preventing an implementation from using some kind of magic to optimize away the storage for the captured empty variable.
That said, doing so would be an ABI break, so don't hold your breath.
Allowing - or requiring - an implementation to make the type of a captured empty variable a base of the closure type, on the other hand, would be a horrendously bad idea. Consider:
struct X { };
struct Y { };
void meow(X x); // #1
void meow(Y y); // #2
void meow(std::function<void()> f); // #3
template<class T, class U>
void purr(T t, U u) {
meow([t = std::move(t), u = std::move(u)] { /* ... */ });
}
It would be insane for purr to do anything other than call #3, yet if captures can become bases then it can call #1, or #2, or be ambiguous.
As others have noted, lambdas are specified to capture as member variables not as bases. So you are out of luck.
What you could do is take a page from bind.
Suppose you have a tuple that does use empty base optimization. Then we can write a helper:
template<class Sig>
struct lambda_ebo_t;
template<class F, class...Args>
struct lambda_ebo_t<F(Args...)>:
private std::tuple<Args...>,
private F
{
decltype(auto) operator()(){
return std::apply( (F&)*this, (std::tuple<Args...>&)*this );
}
template<class...Ts>
lambda_ebo_t( F f, Ts&&...ts ):
std::tuple<Args...>( std::forward<Ts>(ts)... ),
F( std::move(f) )
{}
};
template<class F, class...Args>
lambda_ebo_t<F, std::decay_t<Args>...>
lambda_ebo( F f, Args&&...args ) {
return {std::move(f), std::forward<Args>(args)...};
}
That is a bunch of boilerplate, and incomplete (reference capture may not work right even if you use std::ref), but it gives us:
template <typename FThen>
auto then(FThen&& f_then)
{
return ::node{lambda_ebo([](auto&& p, auto&& t)
{
}, std::move(*this), std::move(f_then))};
}
where we store the data outside the lambda and pass it in as arguments to the lambda. The storage uses EBO.
No need to write a custom EBO class for each lambda, just a few hoops to jump through when you need a lambda with EBO enabled.
This is one without using the tuple, but it doesn't support fundamantal types like int or other things you cannot derive from:
template<class Sig>
struct lambda_ebo_t;
template<class F, class...Args>
struct lambda_ebo_t<F(Args...)>:
private Args...,
// private std::tuple<Args...>,
private F
{
decltype(auto) operator()(){
//return std::apply( (F&)*this, (std::tuple<Args...>&)*this );
return ((F&)(*this))((Args&)*this...);
}
template<class...Ts>
lambda_ebo_t( F f, Ts&&...ts ):
Args(std::forward<Ts>(ts))...,
F( std::move(f) )
{}
};
template<class F, class...Args>
lambda_ebo_t<F(std::decay_t<Args>...)>
lambda_ebo( F f, Args&&...args ) {
return {std::move(f), std::forward<Args>(args)...};
}
Live example, with this test code:
auto test = lambda_ebo( [](auto&&...args){std::cout << sizeof...(args) << "\n";}, []{} , []{}, []{}, []{}, []{}, []{}, []{}, []{}); //
std::cout << "bytes:" << sizeof(test) << "\n";
std::cout << "args:";
test();
sizeof(test) is 1, and it "captures" 8 arguments.
Empty base optimization works for me in the following case
#include <utility>
template <typename F>
class Something : public F {
public:
Something(F&& f_in) : F{std::move(f_in)} {}
};
int main() {
auto something = Something{[]{}};
static_assert(sizeof(decltype(something)) == 1);
}
Live example here https://wandbox.org/permlink/J4m4epDUs19kp5CH
My guess is that the reason it's not working in your case is that the lambda you use in the then() method is not actually empty, it has member variables - the ones listed in your capture. So there is no real empty base there.
If you change the last line of your code to just return node{[]{}} then it works. The lambdas used by .then() do not materialize as "empty" classes.
Whereas in the explicit struct case, it has no member variables per se, only classes it derives from, hence empty base optimization can work there.

Const and non-const functors

This seems like something that ought to be frequently asked and answered, but my search-fu has failed me.
I'm writing a function which I want to accept a generic callable object of some kind (including bare function, hand-rolled functor object, bind, or std::function) and then invoke it within the depths of an algorithm (ie. a lambda).
The function is currently declared like this:
template<typename T, typename F>
size_t do_something(const T& a, const F& f)
{
T internal_value(a);
// do some complicated things
// loop {
// loop {
f(static_cast<const T&>(internal_value), other_stuff);
// do some more things
// }
// }
return 42;
}
I'm accepting the functor by reference because I want to guarantee that it does not get copied on entry to the function, and thus the same instance of the object is actually called. And it's a const reference because this is the only way to accept temporary objects (which are common when using hand-rolled functors or bind).
But this requires that the functor implement operator() as const. I don't want to require that; I want it to be able to accept both.
I know I can declare two copies of this method, one that accepts it as const and one as non-const, in order to cover both cases. But I don't want to do that as the comments are hiding quite a lot of code that I don't want to duplicate (including some loop constructs, so I can't extract them to a secondary method without just moving the problem).
I also know I could probably cheat and const_cast the functor to non-const before I invoke it, but this feels potentially dangerous (and in particular would invoke the wrong method if the functor intentionally implements both const and non-const call operators).
I've considered accepting the functor as a std::function/boost::function, but this feels like a heavy solution to what ought to be a simple problem. (Especially in the case where the functor is supposed to do nothing.)
Is there a "right" way to satisfy these requirements short of duplicating the algorithm?
[Note: I would prefer a solution that does not require C++11, although I am interested in C++11 answers too, as I'm using similar constructs in projects for both languages.]
Have you tried a forwarding layer, to force inference of the qualifier? Let the compiler do the algorithm duplication, through the normal template instantiation mechanism.
template<typename T, typename F>
size_t do_something_impl(const T& a, F& f)
{
T internal_value(a);
const T& c_iv = interval_value;
// do some complicated things
// loop {
// loop {
f(c_iv, other_stuff);
// do some more things
// }
// }
return 42;
}
template<typename T, typename F>
size_t do_something(const T& a, F& f)
{
return do_something_impl<T,F>(a, f);
}
template<typename T, typename F>
size_t do_something(const T& a, const F& f)
{
return do_something_impl<T,const F>(a, f);
}
Demo: http://ideone.com/owj6oB
The wrapper should be completely inlined and have no runtime cost at all, except for the fact that you might end up with more template instantiations (and therefore larger code size), though that will only happen when for types with no operator()() const where both const (or temporary) and non-const functors get passed.
Answer for new relaxed requirements.
In commentary on another answer the OP has clarified/changed the requirements to…
“I'm ok with requiring that if the functor is passed in as a temporary
then it must have an operator() const. I just don't want to limit it
to that, such that if a functor is not passed in as a temporary (and
also not a const non-temporary, of course) then it is allowed to have
a non-const operator(), and this will be called”
This is then not a problem at all: just provide an overload that accepts a temporary.
There are several ways of distinguishing the original basic implementation, e.g. in C++11 an extra default template argument, and in C++03 an extra defaulted ordinary function argument.
But the most clear is IMHO to just give it a different name and then provide an overloaded wrapper:
template<typename T, typename F>
size_t do_something_impl( T const& a, F& f)
{
T internal_value(a);
// do some complicated things
// loop {
// loop {
f(static_cast<const T&>(internal_value), other_stuff);
// do some more things
// }
// }
return 42;
}
template<typename T, typename F>
size_t do_something( T const& a, F const& f)
{ return do_something_impl( a, f ); }
template<typename T, typename F>
size_t do_something( T const& a, F& f)
{ return do_something_impl( a, f ); }
Note: there's no need to specify the do_something_impl instantiation explicitly, since it's inferred from the lvalue arguments.
The main feature of this approach is that it supports simpler calls, at the cost of not supporting a temporary as argument when it has non-const operator().
Original answer:
Your main goal is to avoid copying of the functor, and to accept a temporary as actual argument.
In C++11 you can just use an rvalue reference, &&
For C++03 the problem is a temporary functor instance as actual argument, where that functor has non-const operator().
One solution is to pass the burden to the client code programmer, e.g.
require the actual argument to be an lvalue, not a temporary, or
require explicit specification that the argument is a temporary, then take it as reference to const and use const_cast.
Example:
template<typename T, typename F>
size_t do_something( T const& a, F& f)
{
T internal_value(a);
// do some complicated things
// loop {
// loop {
f(static_cast<const T&>(internal_value), other_stuff);
// do some more things
// }
// }
return 42;
}
enum With_temp { with_temp };
template<typename T, typename F>
size_t do_something( T const& a, With_temp, F const& f )
{
return do_something( a, const_cast<F&>( f ) );
}
If it is desired to directly support temporaries of const type, to ease the client code programmer's life also for this rare case, then one solution is to just add an additional overload:
enum With_const_temp { with_const_temp };
template<typename T, typename F>
size_t do_something( T const& a, With_const_temp, F const& f )
{
return do_something( a, f );
}
Thanks to Steve Jessop and Ben Voigt for discussion about this case.
An alternative and much more general C++03 way is to provide the following two little functions:
template< class Type >
Type const& ref( Type const& v ) { return v; }
template< class Type >
Type& non_const_ref( Type const& v ) { return const_cast<T&>( v ); }
Then do_something, as given above in this answer, can be called like …
do_something( a, ref( MyFunctor() ) );
do_something( a, non_const_ref( MyFunctor() ) );
Why I didn't think of that immediately, in spite of having employed this solution for other things like string building: it's easy to create complexity, harder to simplify! :)