In C++, I have a set of variadic template functions that I'd like to accept an arbitrary number of parameters, either constant references or as r-value references (so I can move things rather than copy when possible). However, I find that the version that accepts the constant reference is called even when I'm wrapping arguments in std::move().
// Accept end of parameters.
void example () {}
// Accept a non-constant R-value reference to do a move
template <typename... More>
void example (std::string &&value, More... parameters) {
std::cout << value << ": moved" << std::endl;
example (parameters...);
}
// Accept a constant reference parameter.
template <typename... More>
void example (const std::string &value, More... parameters) {
std::cout << value << ": copied" << std::endl;
example (parameters...);
}
int main (int, char **) {
std::string first { "first" };
std::string second { "second" };
std::string third { "third" };
std::cout << "Trying variadic with move as second parameter: " << std::endl;
example (first, std::move (second), third);
// std::cout << "Trying variadic with move as first parameter: " << std::endl;
// This next line won't even compile when uncommented
// example (std::move (first), std::move (second), third);
return 0;
}
The output is:
Trying variadic with move as second parameter:
first: copied
second: copied
third: copied
instead of the expected:
Trying variadic with move as second parameter:
first: copied
second: moved
third: copied
And as a bonus, when I wrap the first argument in std::move(), I get a compile error on both g++7 and clang 9.
What am I doing wrong?
There are several problems here:
More... parameters always receives arguments by value (as long as the template parameters are deduced), because types in typename ...More will never be deduced as references.
All arguments passed to example in example(parameters...); will always be lvalues.
The string && overload can't call the const string & one, because it's not yet declared at that point.
Instead of passing by value, you should use forwarding references and std::forward. And you need to declare the const string & overload before defining the string && one.
void example() {}
// Declare lvalue overload.
template <typename ...More>
void example(const std::string &value, More &&... parameters);
// Rvalue overload.
template <typename... More>
void example (std::string &&value, More &&... parameters) {
std::cout << value << ": moved" << std::endl;
example(std::forward<More>(parameters)...);
}
// Lvalue overload.
template <typename ...More>
void example(const std::string &value, More &&... parameters) {
std::cout << value << ": copied" << std::endl;
example(std::forward<More>(parameters)...);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With