在不复制和使用移动语义的情况下初始化向量

Initialize vector without copying and using move semantics

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

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

#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;
}

这会产生以下输出:

Parametrized constructor
Move constructor
Parametrized constructor
Move constructor
Copy constructor
Copy constructor
Destructor
Destructor
Destructor
Destructor
|MSFT|
|TSL|
Destructor
Destructor

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

如果你使用

std::vector<Ticker> table = std::vector<Ticker>{Ticker("MSFT"), Ticker("TSL")};

你会得到

Parametrized constructor
Parametrized constructor
Copy constructor
Copy constructor
Destructor
Destructor
|MSFT|
|TSL|
Destructor
Destructor

其中有 4 个构造函数调用,而不是您当前的 6 个。其中 2 个调用用于 Ticker("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

其输出为

Parametrized constructor
Parametrized constructor
|MSFT|
|TSL|
Destructor
Destructor

如果你想要像 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;
}

然后你会像

一样使用它
auto table = make_vector<Ticker>("MSFT", "TSL");