如何移动initializer_list的元素?

时间:2016-04-02 19:22:48

标签: c++ c++11 move-semantics initializer-list

假设您有一个std::vector<std::string>类型的变量,并使用初始化列表对其进行初始化:

using V = std::vector<std::string>;
V v = { "Hello", "little", "world", "of", "move", "semantics" };

编译器将为每个字符串文字创建一个临时std::string,在这些文件上创建一个初始化列表,然后调用V的ctor并创建向量。 ctor并不知道所有这些字符串都是临时字符串,因此复制每个字符串。

我没有在标准中找到任何允许矢量ctor在临时时移动元素的内容。

我是否遗漏了某些内容或使用初始化程序列表导致不必要的副本?我正在编写类,这个问题可能会导致代码效率低下。任何避免不必要副本的技术都将非常感激。

2 个答案:

答案 0 :(得分:5)

没有办法避免从initializer_list<string>进行复制,因为标准定义了一个构造函数的调用,该构造函数采用初始化列表参数,从花括号初始化器作为实际参数,如下所示(强调添加):

C ++14§8.5.4/ 5
  

类型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"} } );
}

Live example