Reputation: 73
template<class T>
struct IntHolder {
T i;
};
template<class T>
void addOne(T& t) {
t.i += 1;
}
template<class... Args>
void addAll(Args... args) {
// Magic to run addOne on each arg
int dummy[] = { 0, ((void)addOne(std::forward<Args>(args)), 0)... };
}
int main() {
IntHolder<int> x{2};
IntHolder<double> t{3};
addAll(x, t);
return 0;
}
This toy example won't compile because
prog.cc: In instantiation of 'void addAll(Args ...) [with Args = {IntHolder<int>, IntHolder<double>}]':
prog.cc:60:16: required from here
prog.cc:54:39: error: invalid initialization of non-const reference of type 'IntHolder<int>&' from an rvalue of type 'IntHolder<int>'
int dummy[] = { 0, ((void)addOne(std::forward<Args>(args)), 0)... };
^
prog.cc:48:6: note: initializing argument 1 of 'void addOne(T&) [with T = IntHolder<int>]'
void addOne(T& t) {
What I thought would happen here is that addAll gets two lvalues passed in, and then addOne would be called on each of those as an lvalue. However, it seems somewhere along the way, the compiler thinks that the argument is getting converted to an rvalue. Why might this be?
Upvotes: 2
Views: 61
Reputation: 172924
You're declaring parameter as non-reference, i.e. pass-by-value, then when being passed x
and t
, the type would be deduced as IntHolder<int>
and IntHolder<double>
. Then std::forward<Args>
would convert them into rvalues.
For forwarding reference it should be
template<class... Args>
void addAll(Args&&... args) {
// ^^
// Args would be deduced as IntHolder<int>&, IntHolder<double>&
// then std::forward<Args>(args) yields lvalues
int dummy[] = { 0, ((void)addOne(std::forward<Args>(args)), 0)... };
}
Upvotes: 6