初始化向量而不复制和使用移动语义

Blu*_*rin 6 c++ c++17

有没有办法在初始化向量时避免复制?

下面的代码将产生以下输出。

#include <iostream>
#include <vector>
using namespace std;

struct Ticker {
    std::string m_ticker;
    
    Ticker() {
        std::cout << "Default constructor" << std::endl;
        }
    Ticker(const std::string& ticker) 
    : m_ticker(ticker)
    {
        std::cout << "Parametrized constructor" << std::endl;
    }
    Ticker(Ticker&& other) 
    {
        std::cout << "Move constructor" << std::endl;
        m_ticker = other.m_ticker;
        other.m_ticker = "";
    }
    Ticker(const Ticker& x) 
    {
        std::cout << "Copy constructor" << std::endl;
        m_ticker = x.m_ticker;
    }
    ~Ticker() 
    {
    std::cout << "Destructor" << std::endl;
    }
    
    friend std::ostream& operator << (std::ostream& os, const Ticker& dr);
};

std::ostream& operator << (std::ostream& os, const Ticker& dr)
{
  os << "|" << dr.m_ticker << "|";
   return os;
}
    

int main() {
  std::vector<Ticker> table = std::move(std::vector<Ticker>{std::move(Ticker("MSFT")), std::move(Ticker("TSL"))});
  for (const auto& row: table)
  {
    std::cout << row << std::endl;
  }
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

这会产生以下输出:

Parametrized constructor
Move constructor
Parametrized constructor
Move constructor
Copy constructor
Copy constructor
Destructor
Destructor
Destructor
Destructor
|MSFT|
|TSL|
Destructor
Destructor
Run Code Online (Sandbox Code Playgroud)

有没有办法避免复制构造函数并就地初始化或只是移动而不复制?

Nat*_*ica 13

如果你使用

std::vector<Ticker> table = std::vector<Ticker>{Ticker("MSFT"), Ticker("TSL")};
Run Code Online (Sandbox Code Playgroud)

你会得到

Parametrized constructor
Parametrized constructor
Copy constructor
Copy constructor
Destructor
Destructor
|MSFT|
|TSL|
Destructor
Destructor
Run Code Online (Sandbox Code Playgroud)

其中有 4 个构造函数调用,而不是当前的 6 个。其中 2 个调用是 forTicker("MSFT")Ticker("TSL"),然后另外两个副本是因为初始值设定项列表将其中的元素存储为const,因此必须将它们复制到向量中,因为您无法从对象移动const

要获得最少 2 个构造函数调用,您需要使用emplace_back成员函数,例如

std::vector<Ticker> table;      // create empty vector
table.reserve(2);               // allocate space for 2 Tickers but create nothing
table.emplace_back("MSFT");     // directly construct from "MSFT" in the reserved space
table.emplace_back("TSL");      // directly construct from "TSL" in the reserved space
Run Code Online (Sandbox Code Playgroud)

其输出为

Parametrized constructor
Parametrized constructor
|MSFT|
|TSL|
Destructor
Destructor
Run Code Online (Sandbox Code Playgroud)

如果您想要类似的语法std::vector<Ticker> table = std::vector<Ticker>{Ticker("MSFT"), Ticker("TSL")};,但又没有额外的开销,您可以将emplace_back解决方案包装在工厂函数中,例如

template <typename T, typename... Args> 
auto make_vector(Args&&... args)
{
    std::vector<T> data;
    data.reserve(sizeof...(Args));
    (data.emplace_back(std::forward<Args>(args)), ...);
    return data;
}
Run Code Online (Sandbox Code Playgroud)

然后你会像这样使用它

auto table = make_vector<Ticker>("MSFT", "TSL");
Run Code Online (Sandbox Code Playgroud)

  • @BlueTrin你可以像这样抽象出这种丑陋:http://coliru.stacked-crooked.com/a/c7b2acbdbf0aad90 (2认同)
  • @BlueTrin 没问题,很高兴提供帮助。我将该方法添加到答案中,以防链接失效。 (2认同)