std::initializer_list 不可复制类型的替代方案
std::initializer_list alternatives for noncopyable types
我知道尝试使用 std::initializer_list<NonCopyable>
会导致错误,因为元素被复制到由 initializer_list
表示的临时数组中。我还阅读了一些解释,说明为什么在列表中使用右值引用是不对的,我对此很满意。
问题是我想传递不可复制的东西不是为了从它们中移动,而是只是 const
-访问它们,所以关于右值的论点不适用。如果可能的话,我该怎么做才能保留列表初始化语法和引用语义(没有包装器,没有原始指针)?
NonCopyable a{...}, b{...};
ListInitialized c{a, b};
我想我在这里遗漏了一些非常明显的东西。
更新:
有效(*),
ListInitialized(std::initializer_list<std::reference_wrapper<NonCopyable>>) {...}
但不接受右值。如果我可以简单地传递一个可以进入 const NonCopyable&
.
的任何列表,那就太好了
(*) 我知道我写了 "no wrappers" 但这既不影响调用代码也不影响列表的迭代。
您可以给 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);
};
除了上面的评论和答案,我发现这个简约的包装器满足了我的需求:
#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
}
当然,需要注意确保通过此传递的任何右值将在它被引用的时间内一直存在。
我知道尝试使用 std::initializer_list<NonCopyable>
会导致错误,因为元素被复制到由 initializer_list
表示的临时数组中。我还阅读了一些解释,说明为什么在列表中使用右值引用是不对的,我对此很满意。
问题是我想传递不可复制的东西不是为了从它们中移动,而是只是 const
-访问它们,所以关于右值的论点不适用。如果可能的话,我该怎么做才能保留列表初始化语法和引用语义(没有包装器,没有原始指针)?
NonCopyable a{...}, b{...};
ListInitialized c{a, b};
我想我在这里遗漏了一些非常明显的东西。
更新:
有效(*),
ListInitialized(std::initializer_list<std::reference_wrapper<NonCopyable>>) {...}
但不接受右值。如果我可以简单地传递一个可以进入 const NonCopyable&
.
(*) 我知道我写了 "no wrappers" 但这既不影响调用代码也不影响列表的迭代。
您可以给 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);
};
除了上面的评论和答案,我发现这个简约的包装器满足了我的需求:
#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
}
当然,需要注意确保通过此传递的任何右值将在它被引用的时间内一直存在。