[英]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.