簡體   English   中英

std :: shuffle不能用std :: list編譯

[英]std::shuffle doesn't compile with std::list

我試圖改組一些生成的元素列表。 這是代碼:

std::default_random_engine generator (10);
std::list<int> list(10);

int n = 0;
std::generate(list.begin(), list.end(), [&]{ return n++; });
std::shuffle(list.begin(), list.end(), generator);

它沒有編譯。 以下是錯誤:

/include/c++/v1/algorithm:3059:34: Invalid operands to binary expression ('std::__1::__list_iterator<int, void *>' and 'std::__1::__list_iterator<int, void *>')
main.cpp:1:10: In file included from main.cpp:1:

/include/c++/v1/random:1641:10: In file included from /bin/../include/c++/v1/random:1641:

main.cpp:37:10: In instantiation of function template specialization 'std::__1::shuffle<std::__1::__list_iterator<int, void *>, std::__1::linear_congruential_engine<unsigned int, 48271, 0, 2147483647> &>' requested here
/include/c++/v1/iterator:622:1: Candidate template ignored: could not match 'reverse_iterator' against '__list_iterator'
/include/c++/v1/iterator:1017:1: Candidate template ignored: could not match 'move_iterator' against '__list_iterator'
/include/c++/v1/iterator:1369:1: Candidate template ignored: could not match '__wrap_iter' against '__list_iterator'
/include/c++/v1/string:486:11: Candidate template ignored: could not match 'fpos' against '__list_iterator'

有人有什么想法嗎?

std::list不提供對其元素的隨機訪問, std::shuffle()需要。 這就是std::shuffle()的簽名在其規范中的樣子(C ++標准的第25.3.12段):

template<class RandomAccessIterator, class UniformRandomNumberGenerator>
void shuffle(RandomAccessIterator first,
             RandomAccessIterator last,
             UniformRandomNumberGenerator&& g);

如果可以,請考慮使用std::vector - 順便說一句,鼓勵您使用C ++標准本身作為默認順序容器。

作為一個例子( Coliru的現場演示 ):

int main()
{
    std::default_random_engine generator(10);
    std::vector<int> v(10);

    std::iota(begin(v), end(v), 0);
    std::shuffle(begin(v), end(v), generator);

    for (auto x : v) { std::cout << x; }
}

std::iota()算法只是對std::generate特定用法的一種更簡單的替代方法。

std::shuffle需要隨機訪問迭代器。 std::list不提供這些。 您需要一個不同的容器,例如std::vector

如果你真的需要std::list ,你可能需要在專用算法中實現shuffling。 但首先要確保你真的需要它。 通常人們認為當他們真正需要std::vector時他們需要std::list

[algorithms.general] / 2, shuffle聲明:

 template<class RandomAccessIterator, class UniformRandomNumberGenerator> void shuffle(RandomAccessIterator first, RandomAccessIterator last, UniformRandomNumberGenerator&& rand); 

[..]

如果算法的模板參數是RandomAccessIterator [..], 則實際模板參數應滿足隨機訪問迭代器的要求(24.2.7)。

顯然std::list只提供雙向迭代器。 嘗試使用提供隨機訪問迭代器的容器。

AndyProwl正確地解釋了代碼無法編譯的原因,並指出使用std :: vector比使用std :: list更合適。 然而, juanchopanza scaremongers略微聲稱,他們聲稱改組std :: list需要專門的算法。 事實上,通過引用的中間向量(並使用移動構造函數,如果適用),使用std :: shuffle很容易混淆std :: list:

#include <numeric>
#include <random>

template <typename T, typename URBG>
void shuffle(std::list<T>& l, URBG&& urbg)
{
    std::vector<std::reference_wrapper<const T>> v(l.begin(), l.end());
    std::shuffle(v.begin(), v.end(), urbg);
    std::list<T> shuffled;
    for (auto &ref : v) shuffled.push_back(std::move(ref.get()));
    l.swap(shuffled);
}

int main()
{
    std::list<int> l(10);
    std::iota(l.begin(), l.end(), 0);
    shuffle(l, std::mt19937{ std::random_device{}() });
    for (auto x : l) std::cout << x << " ";
}

事實上,我們可以做得更好:因為可以在列表之間移動元素而不會使迭代器或引用無效,我們甚至可以混合既不可復制也不可移動的對象列表,並且(更重要的是)也可以確保洗牌保留引用列出元素。

template <typename T, typename URBG>
void shuffle(std::list<T>& l, URBG&& urbg)
{
    std::vector<std::list<T>::const_iterator> v;
    for (auto it = l.cbegin(); it != l.cend(); ++it) v.push_back(it);
    std::shuffle(v.begin(), v.end(), urbg);
    std::list<T> shuffled;
    for (auto &it : v) shuffled.splice(shuffled.end(), l, it);
    l.swap(shuffled);
}

當然,這兩種解決方案都具有引用(或迭代器)向量的O(n)空間開銷。 如果這是一個問題,那么你可以實現一個較慢的為O(n log n)的使用只是O(1)空間,如所描述的時間算法在這里 雖然你可能通過切換到std :: forward_list來節省更多空間。

暫無
暫無

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

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