альтернативы std :: initializer_list для некопируемых типов - PullRequest
4 голосов
/ 01 апреля 2019

Я знаю, что попытка использовать std::initializer_list<NonCopyable> приводит к ошибке, потому что элементы копируются во временный массив, представленный initializer_list.Я также прочитал некоторые объяснения, почему было бы неправильно иметь в списке ссылки на rvalue, с которыми я согласен.

Проблема в том, что я хотел бы передавать не копируемые вещи, а не двигатьсяиз них, но только const - доступ к ним, поэтому аргумент о значениях не применим.Что я могу сделать, чтобы сохранить, если возможно, синтаксис инициализации списка и семантику ссылок (без оболочек, без необработанных указателей)?

NonCopyable a{...}, b{...};
ListInitialized c{a, b};

Я думаю, что здесь упущено что-то чрезвычайно очевидное.

Обновление:

Это работает (*),

ListInitialized(std::initializer_list<std::reference_wrapper<NonCopyable>>) {...}

, но не принимает значения.Было бы хорошо, если бы я мог просто передать список всего, что могло бы войти в const NonCopyable&.

(*) Я знаю, что написал «без упаковщиков», но это не влияет ни на вызывающий код, ни наитерация по списку.

Ответы [ 2 ]

4 голосов
/ 01 апреля 2019

Вы можете дать ListInitialized шаблон конструктора переменных:

struct ListInitialized
{
  template <class... T>
  ListInitialized(const T... &arg);
};

Если вам нужно убедиться, что он может быть создан только с правильным типом, рассмотрите подходящий SFINAE :

struct ListInitialized
{
  template <
    class... T,
    class Sfinae = std::enable_if_t<std::is_same<std::decay_t<T>, NonCopyable> &&...
  >
  ListInitialized(const T... &arg);
};
0 голосов
/ 05 апреля 2019

В дополнение к комментариям и ответам выше, я обнаружил, что эта минималистическая оболочка удовлетворяет моим потребностям:

#include <initializer_list>
#include <utility>

struct S {
  S() { }
  S(const S&) = delete; // Non-copyable
  void f() const { }
};

template<class T>
class const_reference_wrapper {
public:
  const_reference_wrapper(const T& ref_) : ref(ref_) { }
  operator const T&() const { return ref; }
private:
  const T& ref;
};

struct T {
  T(std::initializer_list<const_reference_wrapper<S>> l) : c(l.size()) {
    for(const S& i : l)  // note: const auto& can't be used here, but it would be the same for std::reference_wrapper
      i.f();  // we can do something with the elements
  }

  int c;
};

int main() {
  S a, b;
  T t{a, b, S{}};  // we can mix lvalues and rvalues with a natural syntax
  return t.c;  // correctly returns 3
}

Конечно, нужно позаботиться о том, чтобы любое значение, пройденное через это, прошло.время, когда на него ссылаются.

...