是否可以为成员初始化提供带有复制省略的构造函数?
Is it possible to provide a constuctor with copy elision for member initialization?
我正在测试使用以下小代码初始化 class 成员的不同模式:
struct S {
S() { std::cout << "ctor\n"; }
S(const S&) { std::cout << "cc\n"; }
S(S&&) noexcept{ std::cout << "mc\n"; }
S& operator=(const S&) { std::cout << "ca\n"; return *this; }
S& operator=(S&&) noexcept{ std::cout << "ma\n"; return *this; }
~S() { std::cout << "dtor\n"; }
};
struct P1 {
S s_;
};
struct P2 {
P2(const S& s) : s_(s) {}
S s_;
};
struct P3 {
P3(const S& s) : s_(s) {}
P3(S&& s) : s_(std::move(s)) {}
S s_;
};
int main() {
S s;
std::cout << "------\n";
{
P1 p{s}; // cc
}
std::cout << "---\n";
{
P1 p{S{}}; // ctor = copy elision
}
std::cout << "------\n";
{
P2 p{s}; // cc
}
std::cout << "---\n";
{
P2 p{S{}}; // ctor + cc
}
std::cout << "------\n";
{
P3 p{s}; // cc
}
std::cout << "---\n";
{
P3 p{S{}}; // ctor + mc
}
std::cout << "------\n";
}
正如您在评论中看到的,只有在 P1{S{}}
复制省略的聚合初始化的情况下,我们的 class 才会在没有任何 copy/move 构造函数调用的情况下进行初始化。我想知道是否有可能提供一个像聚合初始化器一样直接初始化成员的构造函数。有什么想法吗?
更新:
我想知道我是否对标准理解不正确,但根据我的理解,这里发生了一些奇怪的事情:
- 对于初始化列表,我们有:
class-or-identifier ( expression-list(optional) ):
Initializes the base or member named by class-or-identifier using direct initialization or, if expression-list is empty, value-initialization
- 对于直接初始化,我们有:
If T is a class type, if the initializer is a prvalue expression whose type is the same class as T (ignoring cv-qualification), the initializer expression itself, rather than a temporary materialized from it, is used to initialize the destination object. (copy elision)
因此,我认为对于像 s_(std::move(s))
这样的初始化列表应该进行复制省略,不是吗?
I wonder if it is possible to provide a constructor which initialize members directly like aggregate initializer.
当然可以。编写一个构造函数,它不接受成员类型的参数,而是接受转发给成员构造函数的参数。在您的情况下,成员类型是默认可构造的,因此您不需要转发任何参数:
struct P4 {
P4(): s() {}
S s;
};
我正在测试使用以下小代码初始化 class 成员的不同模式:
struct S {
S() { std::cout << "ctor\n"; }
S(const S&) { std::cout << "cc\n"; }
S(S&&) noexcept{ std::cout << "mc\n"; }
S& operator=(const S&) { std::cout << "ca\n"; return *this; }
S& operator=(S&&) noexcept{ std::cout << "ma\n"; return *this; }
~S() { std::cout << "dtor\n"; }
};
struct P1 {
S s_;
};
struct P2 {
P2(const S& s) : s_(s) {}
S s_;
};
struct P3 {
P3(const S& s) : s_(s) {}
P3(S&& s) : s_(std::move(s)) {}
S s_;
};
int main() {
S s;
std::cout << "------\n";
{
P1 p{s}; // cc
}
std::cout << "---\n";
{
P1 p{S{}}; // ctor = copy elision
}
std::cout << "------\n";
{
P2 p{s}; // cc
}
std::cout << "---\n";
{
P2 p{S{}}; // ctor + cc
}
std::cout << "------\n";
{
P3 p{s}; // cc
}
std::cout << "---\n";
{
P3 p{S{}}; // ctor + mc
}
std::cout << "------\n";
}
正如您在评论中看到的,只有在 P1{S{}}
复制省略的聚合初始化的情况下,我们的 class 才会在没有任何 copy/move 构造函数调用的情况下进行初始化。我想知道是否有可能提供一个像聚合初始化器一样直接初始化成员的构造函数。有什么想法吗?
更新: 我想知道我是否对标准理解不正确,但根据我的理解,这里发生了一些奇怪的事情:
- 对于初始化列表,我们有:
class-or-identifier ( expression-list(optional) ): Initializes the base or member named by class-or-identifier using direct initialization or, if expression-list is empty, value-initialization
- 对于直接初始化,我们有:
If T is a class type, if the initializer is a prvalue expression whose type is the same class as T (ignoring cv-qualification), the initializer expression itself, rather than a temporary materialized from it, is used to initialize the destination object. (copy elision)
因此,我认为对于像 s_(std::move(s))
这样的初始化列表应该进行复制省略,不是吗?
I wonder if it is possible to provide a constructor which initialize members directly like aggregate initializer.
当然可以。编写一个构造函数,它不接受成员类型的参数,而是接受转发给成员构造函数的参数。在您的情况下,成员类型是默认可构造的,因此您不需要转发任何参数:
struct P4 {
P4(): s() {}
S s;
};