Принять все комбинаторные возможности для набора типов объектов в качестве параметра для функции в C ++ - PullRequest
3 голосов
/ 07 апреля 2020

Учитывая три различных пустых structs, A, B и C, я хочу, чтобы функция foo принимала любую комбинацию любого числа из этих трех параметров, например:

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{});

Я думал, что шаблоны variadi c и перегрузка функций помогут в этой ситуации, поэтому вот что я попробовал:

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

Однако при вызове f(2, A{}, B{}), только x = 2 with A получает распечатаны. Я думаю, что понимаю, почему это не работает, но я не совсем уверен, как мне на самом деле иметь дело с этой ситуацией.

EDIT Код, который я тестировал, использовал ссылку на rvalue для тип считается известным, например:

template <typename... ExtraParams>
void foo(int x, A&&, ExtraParams&&... extra)
{
  foo(x, extra...);
  std::cout << " with A";
}

, и это сгенерирует точное поведение, о котором я упоминал (хотя я не знаю почему).

1 Ответ

2 голосов
/ 07 апреля 2020

Проблема в том, что когда вы передаете параметры extra другим перегрузкам foo как foo(x, extra...);, они lvalues ​​ и не могут быть привязаны к ссылкам rvalue, таким как A&& и B&&.

(выделено мной)

Следующие выражения являются выражениями lvalue:

  • имя переменной, функция , a template parameter object (since C++20), или член данных, независимо от типа, такой как std :: cin или std :: endl. Даже если тип переменной является rvalue ссылкой, выражение, состоящее из ее имени, является выражением lvalue ;

Вы должны использовать std::forward переслать параметры (как r-значения, если переданные исходные аргументы являются r-значениями, т. е. использовать ссылка для пересылки ). например,

foo(x, std::forward<ExtraParams>(extra)...);

LIVE

...