使用临时成员初始化成员时,如何确保对临时构造函数的单一调用?

When initialising member with temporary, how to ensure a single call to temporary's constructor?

假设我有一个包装器:

template<typename T>
struct Outer {
    T inner;
    ...
};

,我想创建一个 Outer 包装一个 Inner,像这样:

Outer<Inner> wrapper(Inner(...)); // Inner object is a temporary

是否可以声明 Outer/Inner 以便从临时 Inner 创建 Outer 对象仅涉及构建单个 Inner 目的?我尝试为 Inner 声明移动构造函数,并让 Outer 获取对 Inner 的右值引用,甚至在临时但仍然是 2 个副本上显式调用 std::move Inner 已创建:

#include <stdio.h>
#include <utility>

template<typename T>
struct Outer {
    T inner;
    Outer(T&& inner) : inner(inner) {}
};

struct Inner {
    int x;
    int y;

    Inner(const int x, const int y) : x(x), y(y) { printf("ctor\n"); }
    Inner(const Inner& rhs) : x(rhs.x), y(rhs.y) { printf("copy ctor\n"); }
    Inner(Inner&& rhs) : x(std::exchange(rhs.x, 0)), y(std::exchange(rhs.y, 0)) {
        printf("move ctor\n");
    }
    Inner& operator=(const Inner& rhs) { 
        printf("assign\n"); 
        return *this = Inner(rhs); 
    }
    Inner& operator=(Inner&& rhs) { 
        printf("move assign\n");
        std::swap(x, rhs.x);
        std::swap(y, rhs.y);
        return *this;
    }
    ~Inner() { printf("dtor\n"); }
};

int main() {
    Outer<Inner> wrapper(Inner(123, 234));
    // SAME: Outer<Inner> wrapper(std::move(Inner(123, 234)));
    return 0;
}

输出:

ctor <-- The temporary
copy ctor <-- Yet another instance, from the temporary
dtor
dtor

如上所示,创建一个Outer个实例就创建了2个Inners。这看起来很奇怪,因为我认为可以省略临时文件,或者至少调用移动构造函数。是否可以使用单个 Inner 构造来实例化 Outer

我猜你正在寻找这个:

#include <stdio.h>
#include <utility>

template <typename T> struct Outer {
    T inner;
    // Outer(T &&inner) : inner(std::move(inner)) {}
    template <class... Args> Outer(Args &&...args) :inner(args...) {}
};

struct Inner {
    int x;
    int y;

    Inner(const int x, const int y) : x(x), y(y) { printf("ctor\n"); }
    Inner(const Inner &rhs) : x(rhs.x), y(rhs.y) { printf("copy ctor\n"); }
    Inner(Inner &&rhs)
        : x(std::exchange(rhs.x, 0)), y(std::exchange(rhs.y, 0)) {
        printf("move ctor\n");
    }
    Inner &operator=(const Inner &rhs) {
        printf("assign\n");
        return *this = Inner(rhs);
    }
    Inner &operator=(Inner &&rhs) {
        printf("move assign\n");
        std::swap(x, rhs.x);
        std::swap(y, rhs.y);
        return *this;
    }
    ~Inner() { printf("dtor\n"); }
};

int main() {
    Outer<Inner> wrapper(123, 234);
    // SAME: Outer<Inner> wrapper(std::move(Inner(123, 234)));
    return 0;
}