假设您有一个std::vector<std::string>
类型的变量,并使用初始化列表对其进行初始化:
using V = std::vector<std::string>;
V v = { "Hello", "little", "world", "of", "move", "semantics" };
编译器将为每个字符串文字创建一个临时std::string
,在这些文件上创建一个初始化列表,然后调用V
的ctor并创建向量。 ctor并不知道所有这些字符串都是临时字符串,因此复制每个字符串。
我没有在标准中找到任何允许矢量ctor在临时时移动元素的内容。
我是否遗漏了某些内容或使用初始化程序列表导致不必要的副本?我正在编写类,这个问题可能会导致代码效率低下。任何避免不必要副本的技术都将非常感激。
答案 0 :(得分:5)
没有办法避免从initializer_list<string>
进行复制,因为标准定义了一个构造函数的调用,该构造函数采用初始化列表参数,从花括号初始化器作为实际参数,如下所示(强调添加):
“类型
std::initializer_list<E>
的对象是从初始化列表构造的,就好像实现分配了一个{strong> {{1}类型的N
元素的临时数组一样} ,其中const E
是元素的数量 初始化列表
恕我直言,这真的很不幸。
解决方法(对于您自己的类)是接受N
。
以下是适用于initializer_list<char const*>
的变通方法的示例。为此,在不控制类的代码的情况下,它涉及明确声明数据数组(实际上是std::vector<string>
)。这与初始化程序列表机制旨在避免的C ++ 03一样:
initializer_list
输出:
Making vector a. My_string(*) <- 'a1' My_string(*) <- 'a2' My_string(*) <- 'a3' My_string(const&) <- 'a1' My_string(const&) <- 'a2' My_string(const&) <- 'a3' Making data for vector b. Making vector b. My_string(*) <- 'b1' My_string(*) <- 'b2' My_string(*) <- 'b3'
答案 1 :(得分:3)
经过一番思考后,我想出了一个基于mutable
的解决方案。另一个答案仍然大多是正确的,但是可以创建一个带有可变成员的代理来摆脱顶级const
- 然后从那里移动元素。因此,获取初始化列表的方法应该重载const-ref初始化列表和rvalue-ref版本以了解何时允许它们移动。
这是一个有效的例子,起初看起来可能是任意的,但在我的实际用例中,它解决了这个问题。
#include <iostream>
#include <vector>
// to show which operations are called
struct my_string
{
const char* s_;
my_string( const char* s ) : s_( s ) { std::cout << "my_string(const char*) " << s_ << std::endl; }
my_string( const my_string& m ) : s_( m.s_ ) { std::cout << "my_string(const my_string&) " << s_ << std::endl; }
my_string( my_string&& m ) noexcept : s_( m.s_ ) { std::cout << "my_string(my_string&&) " << s_ << std::endl; }
~my_string() { std::cout << "~my_string() " << s_ << std::endl; }
};
// the proxy
struct my_string_proxy
{
mutable my_string s_;
// add all ctors needed to initialize my_string
my_string_proxy( const char* s ) : s_( s ) {}
};
// functions/methods should be overloaded
// for the initializer list versions
void insert( std::vector<my_string>& v, const std::initializer_list<my_string_proxy>& il )
{
for( auto& e : il ) {
v.push_back( e.s_ );
}
}
void insert( std::vector<my_string>& v, std::initializer_list<my_string_proxy>&& il )
{
for( auto& e : il ) {
v.push_back( std::move( e.s_ ) );
}
}
int main()
{
std::vector<my_string> words;
insert( words, { {"Hello"}, {"initializer"}, {"with"}, {"move"}, {"support"} } );
}