简体   繁体   English

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

[英]std::initializer_list alternatives for noncopyable types

I know that trying to use a std::initializer_list<NonCopyable> leads to an error because the elements are copied into the temporary array represented by the initializer_list . 我知道尝试使用std::initializer_list<NonCopyable>会导致错误,因为元素被复制到initializer_list表示的临时数组中。 I have also read some explanation on why it would not be alright to have rvalue references in the list, which I'm fine with. 我还读了一些解释为什么在列表中有rvalue引用是不行的,我很好。

The problem is that I would like to pass noncopyable things not in order to move from them, but only const -access them, so the argument about rvalues does not apply. 问题是我想传递不可复制的东西不是为了从它们移动,而只是const -access它们,所以关于rvalues的参数不适用。 What can I do to retain, if possible, the list initialization syntax and the reference semantics (no wrappers, no raw pointers)? 如果可能,我可以做什么来保留列表初始化语法和引用语义(没有包装器,没有原始指针)?

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

I think I'm missing something extremely obvious here. 我想我错过了一些非常明显的东西。

Update: 更新:

This works(*), 这有效(*),

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

but won't accept rvalues. 但不接受右值。 It would be nice if I could simply pass a list of anything that could go into const NonCopyable& . 如果我能简单地传递一个可以进入const NonCopyable&的任何东西的列表,那将是很好的。

(*) I know I wrote "no wrappers" but this affects neither the calling code nor the iteration over the list. (*)我知道我写了“没有包装器”但这既不影响调用代码也不影响列表上的迭代。

You can give ListInitialized a variadic constructor template: 您可以为ListInitialized提供可变参数构造函数模板:

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

If you need to make sure it can only be instantiated with the correct type, consider suitable SFINAE : 如果您需要确保它只能使用正确的类型进行实例化,请考虑使用合适的SFINAE

struct ListInitialized
{
  template <
    class... T,
    class Sfinae = std::enable_if_t<std::is_same<std::decay_t<T>, NonCopyable> &&...
  >
  ListInitialized(const T... &arg);
};

In addition to the comments and answers above, I found that this minimalistic wrapper fulfills my needs: 除了上面的评论和答案,我发现这个简约的包装器满足了我的需求:

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

Of course, care needs to be taken to ensure that any rvalue passed through this will live through the time it is being referenced. 当然,需要注意确保通过它的任何右值都将在其被引用的时间内存在。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM