Given three different empty structs
, A
, B
and C
, I want to have a function foo
accept any combination of any number of these three parameters, e.g.:
struct A {};
struct B {};
struct C {};
// Foo has a required parameter that must be the first one given. Anything else is optional.
foo(1);
foo(1, A{});
foo(1, B{});
foo(1, B{}, A{});
foo(1, A{}, C{}, B{});
I thought variadic templates and function overloading would help in this situation, so here's what I've tried:
struct A {};
struct B {};
struct C {};
template <typename... ExtraParams>
void foo(int x, ExtraParams&&...)
{
std::cout << "x = " << x;
}
template <typename... ExtraParams>
void foo(int x, A&&, ExtraParams&&... extra)
{
foo(x, extra...);
std::cout << " with A";
}
template <typename... ExtraParams>
void foo(int x, B&&, ExtraParams&&... extra)
{
foo(x, extra...);
std::cout << " with B";
}
// same for C
However, when calling f(2, A{}, B{})
, only x = 2 with A
gets printed. I think I undestand why this does not work, but I am not quite sure how I should actually be dealing with this situation.
EDIT The code I was testing against used rvalue reference for the type considered known, e.g.:
template <typename... ExtraParams>
void foo(int x, A&&, ExtraParams&&... extra)
{
foo(x, extra...);
std::cout << " with A";
}
and this will generate the exact behavior I mentioned (I don't know why though).
The problem is when you pass the parameters extra
to other overloads of foo
as foo(x, extra...);
, they're lvalues and can't be bound to the rvalue-references like A&&
and B&&
.
(emphasis mine)
The following expressions are lvalue expressions:
- the name of a variable, a function
, a template parameter object (since C++20)
, or a data member, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;
You should use std::forward
to forward the parameters (as rvalues if the original arguments passed are rvalues, i.e. to take advantage of forwarding reference). e.g.
foo(x, std::forward<ExtraParams>(extra)...);