C++ 如何从临时变量中初始化非静态私有模板成员变量,即不进行复制或移动?

C++ 如何从临时变量中初始化非静态私有模板成员变量,即不进行复制或移动?,c++,c++11,initialization,c++17,copy-elision,C++,C++11,Initialization,C++17,Copy Elision,我想从临时就地初始化类模板的两个非静态私有模板成员变量,即不进行复制或移动 为澄清,请考虑下面的示例代码: #include <iostream> struct P { P(int n) : n_ { n } {}; P(P&&) { std::cout << "P:moved" << std::endl; } P(const P&) { std::cout << "P:copied" <&

我想从临时就地初始化类模板的两个非静态私有模板成员变量,即不进行复制或移动

为澄清,请考虑下面的示例代码:

#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;
}
#包括
结构P{
P(int n):n{n}{};

P(P&&{std::cout

正如plexando在评论中已经指出的那样,我们不能达到既不移动也不复制的程度。通过不在main主体(在调用站点)中构造任何对象P或Q,而是在X::X(u,v)中构造参数u和v,可以省略一次复制必须有效,从中移动或复制值。这不能忽略

我们所能做的最好的事情就是让X::X使用通用引用,然后向前,这将导致执行移动而不是复制

X(U&&U,V&&V):U{std::forward(U)},V{std::forward(V)}{}

这张照片为我移动了两次。不过,还有一个选项类似于标准容器::emplace中的参数如何转发。使用一些恶心的std::enable_if巫术,您可以编写这个构造函数

模板
X(TO_&&TO_,TO_V&&TO_V):U(std::forward(TO_)),V(std::forward(TO_)){}

这会将任何构造推迟到最新的时刻。在这种情况下,不会打印移动或复制数据,如果数据成员u_u和v_u不能从传递的参数构造,则会将其删除。但是,这取决于您是否适用于您的问题,或者您的类太复杂,无法以这种方式构造它们


TLDR:如果你不能通过尽可能长时间地完美地记录参数来推迟构造,你总是会复制或移动,因为复制省略不会达到这个程度。

基本上要做你想做的事情,你需要构建一种接口,
std::pair
用来转发e成员到成员。他们这样做的方式是构建一个参数元组,然后将这些元组委托给另一个构造函数,该构造函数也会获取每个元组参数包大小的
std::integer_sequence
,以便解包元组使用这些序列直接调用成员构造函数。下面的代码不是p完美,但它会让您开始构建生产版本

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
}
模板
X类{
公众:
//制作副本的旧构造函数

X(uu,vv):uu{U},vu{V}{std::cout“我如何在不复制或移动的情况下初始化…”-如果不是通过直接初始化或复制或移动,您希望如何初始化任何东西??如果您要向
P
Q
添加构造函数,这些构造函数使用
int
s,而不是对象,以及一个用于
X
的构造函数使用两个
int
s,那么您可以使用这些
int
s直接初始化m余烬
u_
v_
@JesperJuhl聚合初始化,复制省略,…但不幸的是,这里不适用。我还考虑将P和Q的构造函数参数作为元组传递到X中,以便“嵌入”P和Q在X的构造函数中。但我希望有人能想出一个不那么麻烦的解决方案。缺少CTAD使它更笨拙。不过你的答案解决了这个问题。谢谢。