2016-05-10 3 views
8

Когда следующий код:Можно ли сортировать вектор кортежей ссылок?

std::vector<std::tuple<int&>> v; 
int a = 5; v.emplace_back(a); 
int b = 4; v.emplace_back(b); 
int c = 3; v.emplace_back(c); 
int d = 2; v.emplace_back(d); 
int e = 1; v.emplace_back(e); 
std::sort(std::begin(v), std::end(v)); 

скомпилирован с GCC/libstdC++ против лязга/LibC++ двоичных дают разные результаты.

Для gcc/libstdc++ один элемент копируется во все другие ссылки.

5 4 3 2 1 
5 5 5 5 5 

Сначала я подумал, что clang/libc++ ведет себя , как и ожидалось, но это работает только до 5 элементов в векторе (причины есть частный случай для небольших контейнеров).

5 4 3 2 1 
1 2 3 4 5 

При прохождении большего количества элементов результат аналогичен gcc.

5 4 3 2 1 0 
3 4 5 5 5 5 

Так это справедливо, чтобы использовать std::sort для контейнера кортежей со ссылками (т.е. сделанных с std::tie, сортировкой подмножества структуры)? Если нет, следует ли ожидать каких-либо предупреждений?

ответ

6

Так это справедливо, чтобы использовать std::sort для контейнера кортежей со ссылками (т.е. сделанных с std::tie, сортировкой подмножества структуры)? Если нет, следует ли ожидать каких-либо предупреждений?

Нет, и нет. Одним из требований типа на std::sort() является то, что:

  • Тип разыменованного RandomIt должен отвечать требованиям MoveAssignable и MoveConstructible.

, где требуется MoveAssignable в выражении t = rv:

Значение t является эквивалентного к значению rv перед назначением.

Но std::tuple<int&> не MoveAssignable потому int& не MoveAssignable. Если вы просто:

int& ra = a; 
int& rb = b; 

ra = std::move(rb); 

Значение ra не эквивалентно уровню стоимости rb. ra еще ссылается на на a, он не изменяется для ссылки на b - что фактически изменилось, было значение a.

Поскольку наш тип не соответствует предварительному условию std::sort(), результатом вызова std::sort() является просто неопределенное поведение.


Обратите внимание, что вы могли сортировать std::vector<std::tuple<std::reference_wrapper<int>>> хотя, потому что std::reference_wrapper является MoveAssignable.

Обратите внимание, что это напоминает неспособность сортировать контейнер auto_ptr, за старый травяной саттер article.

+0

Использование 'std :: reference_wrapper ' работает, потому что оно может быть построено из 'std :: tie', но для получения списка типов T & ... требуется украшение шаблоном шаблона. [Оба компиляторы] (http://melpon.org/wandbox/permlink/atQyUIr1Bh5g6qgn) согласен, что 'станд :: кортеж ' is_move_assignable && is_move_constructible (со стандартными чертами) –

+0

@KarolWozniak Признак может проверить, если только фактическое семантические произведения - вы * можете * переместить присваивание 'tuple '. У этого просто нет фактического поведения, которое эта операция должна иметь, чтобы считаться MoveAssignable. – Barry

+0

@KarolWozniak Например, 'struct X {int val; X & operator = (X &&) {return * this; }; 'is_move_assignable ' истинно, но 'X' на самом деле не MoveAssignable, поскольку это требование не выполняется. – Barry