不可复制类型的std :: initializer_list替代

时间:2019-04-01 09:45:51

标签: c++ c++11 list-initialization

我知道尝试使用std::initializer_list<NonCopyable>会导致错误,因为元素已复制到initializer_list表示的临时数组中。我还阅读了一些解释,以解释为什么在列表中包含右值引用不是很好,我很好。

问题是我不想传递不可复制的东西,而不是为了传递它们,而只是const-访问它们,因此关于rvalues的论点不适用。如果可能的话,我该怎么做才能保留列表初始化语法和引用语义(没有包装器,没有原始指针)?

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

我想我在这里遗漏了一些非常明显的东西。

更新

这有效(*),

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

,但不接受右值。如果我可以简单地传递一个可以放入const NonCopyable&的所有内容的列表,那就太好了。

(*)我知道我写了“ no wrappers”,但这既不影响调用代码也不影响列表的迭代。

2 个答案:

答案 0 :(得分:4)

您可以为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);
};

答案 1 :(得分:0)

除了上面的评论和回答,我发现这个简约的包装器还可以满足我的需求:

#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
}

当然,需要注意确保通过它传递的任何右值在引用之前一直存在。