如何初始化非静态模板成员变量从临时工开始,即而无需复制或移动
How can I initialize non-static private template member variables from temporaries in-place, i.e., without making a copy or move?
我想初始化两个非静态私有模板成员变量,即从临时的就地开始,即不进行副本或移动。
要澄清,请考虑以下示例代码:
#include <iostream>
struct P {
P(int n) : n_ { n } {};
P(P&&) { std::cout << "P:moved" << std::endl; }
P(const P&) { std::cout << "P:copied" << std::endl; }
int n_;
};
struct Q {
Q(double x) : x_ { x } {};
Q(Q&&) { std::cout << "Q:moved" << std::endl; }
Q(const Q&) { std::cout << "Q:copied" << std::endl; }
double x_;
};
/* note that P and Q are just two illustrative examples;
don't count on anything specific in them; with respect
to the asked question, they should just represent two
arbitrary classes with arbitrary ctors */
template<typename U, typename V>
class X {
public:
X(U u, V v) : u_ { u }, v_ { v } {}
private:
U u_;
V v_;
};
int
main(
) {
X x { P { 0 }, Q { 0.0 } };
return 0;
}
输出(带有GCC 8.2.0(是P:copied Q:copied
,因为U和V分别在X的CTOR中复制到U_和V_。但是,由于临时p {0}和q {0.0}仅用于初始化u_和v_,所以我想知道是否可以在就位初始化两个成员变量。我想在这里看到copied
和moved
。更重要的是,我想使用p和q删除的复制和移动CTOR运行此代码。
这在C 17(或更早(中是否可以,如果是,则如何?
基本上要做您想做的事情,您需要构建std::pair
使用的界面将成员的构造函数转发给成员的参数。他们这样做的方式是建立一个参数的元组,然后将这些元组委派给另一个构造函数,该构造器也获得了每个元组参数包的大小的std::integer_sequence
,因此它可以解开元组的包装使用这些序列,将这些序列直接调用成员构造函数。以下代码并不完美,但它将使您踏上构建生产版本的路径。
template<typename U, typename V>
class X {
public:
// old constructor that makes copies
X(U u, V v) : u_ { u }, v_ { v } { std::cout << "X(U, V)n"; }
// this is the constructor the user code will call
template<typename... Args1, typename... Args2>
X(std::piecewise_construct_t pc, std::tuple<Args1...>&& u, std::tuple<Args2...>&& v) :
X(pc, std::move(u), std::move(v), std::make_index_sequence<sizeof...(Args1)>{}, std::make_index_sequence<sizeof...(Args2)>{}) {}
// this is where the magic happens Now that we have Seq1 and Seq2 we can
// unpack the tuples into the constructor
template<typename... Args1, typename... Args2, auto... Seq1, auto... Seq2>
X(std::piecewise_construct_t pc, std::tuple<Args1...>&& u, std::tuple<Args2...>&& v, std::integer_sequence<size_t, Seq1...>, std::integer_sequence<size_t, Seq2...>) :
u_ { std::get<Seq1>(u)... }, v_ { std::get<Seq2>(v)... } {}
private:
U u_;
V v_;
};
int main()
{
// and now we build an `X` by saying we want the tuple overload and building the tuples
X<P,Q> x { std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple(0.0) };
// Unfortunetly we don't get CTAD with this. Not sure if that can be fixed with a deduction guide
}
您还可以查看诸如libc 或libstdc 之类的OpenSource C 库之一,以查看它们如何实现std::pair
的分段构造函数,以获取如何使其值得。
正如Plexando在评论中已经指出的那样,我们既不能移动也不能复制。通过不在Main正文中构造任何对象P或Q(在呼叫站点上(,但是X :: X :: X(U U,V(必须有效,可以从中启用一个复制,而值要么被复制。这是无法散发的。
我们能做的最好的方法是使X :: X使用通用引用,然后向前进行,这将导致移动代替执行副本。
X(U&& u, V&& v) : u_{ std::forward<U&&>(u) }, v_{ std::forward<V&&>(v) } {}
这张印刷给我两次。但是,还有另一种选择类似于standard_container中的参数:: emplace中的参数。使用一些令人恶心的std :: enable_if巫术,您可以编写此构造函数
template<typename TO_U, typename TO_V, typename = std::enable_if_t<std::is_constructible_v<U, TO_U> && std::is_constructible_v<V, TO_V>>>
X(TO_U&& to_u, TO_V&& to_v) : u_(std::forward<TO_U&&>(to_u)), v_(std::forward<TO_V&&>(to_v)) {}
哪些结构都推迟到最新时刻。在这种情况下,没有移动的移动或已复制打印,如果数据成员u_和v_无法从传递的参数构造,则它将被删除。但是,这对您来说是UT,无论是适用于您的问题还是您的课程太复杂,无法以这种方式构造它们。
tldr:如果您无法通过尽可能长时间完美地构建参数推迟构造,则您将始终复制或移动,因为复制Elision不会到达远处。
- 当回溯以零开始时,如何调试崩溃
- 将对象移动到std::shared_ptr
- 何时在引用或唯一指针上使用移动语义
- 如何从具有移动语义的类对象中生成共享指针
- 将shared_ptr移动到<StructA>shared_ptr<变体<结构A、结构 B>>
- C / C++ 移位/偏移/向左或向右移动位图?
- MSVC将仅移动结构参数解释为指针
- 自定义先决条件对移动分配运算符有效吗
- 返回值优化:显式移动还是隐式
- 当有分配器意识的容器被复制/移动时,反弹分配器是否被复制/移走
- 为什么复制而不是移动数据元素?
- 可以使用移动语义更改或改进此C++代码吗?
- 使lambda不可复制/不可移动
- c++在使用指针时移动语义
- 将QGraphicsItem的移动区域限制在多边形区域内
- 如何将到达图形视图右侧(末端)的QGraphicsPixmapItem移动到左侧(开始)侧(就像在贪吃蛇游戏中发生的事情
- 如何初始化非静态模板成员变量从临时工开始,即而无需复制或移动
- 移动数组指针未更改开始的地址
- C 11 STL容器支持仅移动类型,并且具有O(1)写入访问的开始和结束
- 移动鼠标位置,然后回到开始位置