c++ - 如何从就地临时变量初始化非静态私有(private)模板成员变量,即不进行复制或移动?

标签 c++ c++11 initialization c++17 copy-elision

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

为了清楚起见,请考虑以下示例代码:

#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 的构造函数中的 u_ 和 v_。但是,由于临时变量 P { 0 } 和 Q { 0.0 } 仅分别用于初始化 u_ 和 v_,我想知道是否可以就地初始化两个成员变量。我不希望在此处看到 copiedmoved。更重要的是,我想在删除 P 和 Q 的复制和移动构造函数的情况下运行此代码。

这在 C++17(或更早版本)中是否可行,如果可行,如何实现?

最佳答案

基本上,为了做您想做的事,您需要构建一种接口(interface),std::pair 用于将成员的构造函数的参数转发给成员。他们这样做的方法是构建一个参数元组,然后将这些元组委托(delegate)给另一个构造函数,该构造函数还获取每个元组参数包大小的 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++ 等开源 C++ 库之一,了解它们如何实现 std::pair 的分段构造函数,从而了解如何使其具有生产值(value)。

关于c++ - 如何从就地临时变量初始化非静态私有(private)模板成员变量,即不进行复制或移动?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57366122/

相关文章:

java - 使用自定义渲染器初始化动态 JTable 的最佳方法是什么?

Unix/Mac OS X 上的 C++ UTF-8 支持

c++ - 库文件结构的常见做法

c++ - 无类型的连续内存容器

c++ - c++0x 的正式名称是什么?

c - 执行 typedef 时初始化结构数组

c++ - 在另一个全局仿函数中引用全局仿函数

c++ - 在源文件 (cpp) 中定义带有静态和内联的模板函数有哪些优势?

c++ - 如何调用模板类型的正确构造函数?

c++ - 带有指针构造函数的指针