簡體   English   中英

為什么std :: vector在初始化時強制復制?

[英]Why does std::vector enforce copy on initialization?

我有一個復制/移動探測課:

#include <iostream>

struct A
{
    A()
    {
        std::cout << "Creating A" << std::endl;
    }

    ~A() noexcept
    {
        std::cout << "Deleting A" << std::endl;
    }

    A(const A &)
    {
        std::cout << "Copying A" << std::endl;
    }

    A(A &&) noexcept
    {
        std::cout << "Moving A" << std::endl;
    }

    A &operator=(const A &)
    {
        std::cout << "Copy-assigning A" << std::endl;
        return *this;
    }

    A &operator=(A &&) noexcept
    {
        std::cout << "Move-assigning A" << std::endl;
        return *this;
    }
};

我發現跑步:

#include <vector>

int main(int, char **)
{
    std::vector<A> v { A() };
}

產生以下輸出:

Creating A
Copying A
Deleting A
Deleting A

為什么初始化不會移動對象? 我知道std::vector可能會在resize上創建不需要的副本 ,但正如你所看到的,添加noexcept在這里沒有幫助(此外,我不認為調整大小導致副本適用於初始化的原因)。

如果我改為做以下事情:

std::vector<A> v;
v.push_back(A());

我沒有復印件。

用GCC 5.4和Clang 3.8測試。

這不是std::vector ,而是std::initializer_list

std::initializer_listconst數組元素支持。 它不允許非const訪問其數據。

這會阻止其數據移動。

但這是C ++,所以我們可以解決這個問題:

template<class T, class A=std::allocator<T>, class...Args>
std::vector<T,A> make_vector(Args&&...args) {
  std::array<T, sizeof...(Args)> tmp = {{std::forward<Args>(args)...}};
  std::vector<T,A> v{ std::make_move_iterator(tmp.begin()), std::make_move_iterator(tmp.end()) };
  return v;
}

現在我們得到:

auto v = make_vector<A>( A() );

為每個元素提供1個額外的移動:

Creating A
Moving A
Moving A
Deleting A
Deleting A
Deleting A

我們可以通過仔細保留和回放來消除這個額外的實例:

template<class T, class A=std::allocator<T>, class...Args>
std::vector<T,A> make_vector(Args&&...args) {
  std::vector<T,A> v;
  v.reserve(sizeof...(args));
  using discard=int[];
  (void)discard{0,(void(
    v.emplace_back( std::forward<Args>(args) )
  ),0)...};
  return v;
}

兩者的實例 - 簡單地交換v2:: for v1::以查看第一個實際操作。

輸出:

Creating A
Moving A
Deleting A
Deleting A

這里可能會有更多的向量開銷,因為編譯器可能很難證明emplace_back不會導致重新分配(即使我們可以證明它),因此最有可能編譯冗余檢查。 (在我看來,如果沒有足夠的容量,我們需要一個emplace_back_unsafe ,即UB)。

失去額外的A s可能是值得的。

另一個選擇:

template<std::size_t N, class T, class A=std::allocator<T>, class...Args>
std::vector<T,A> make_vector(std::array<T, N> elements) {
  std::vector<T,A> v{ std::make_move_iterator(elements.begin()), std::make_move_iterator(elements.end()) };
  return v;
}

用的像

auto v = make_vector<1,A>({{ A() }});

您必須手動指定多少元素。 它與上面的版本2一樣高效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM