Неявное преобразование в явные логические типы для сортировки контейнеров?

Я играю с новым explicit для каст-операторов. Если вы напишете что-то вроде

struct Data {
    explicit operator string(); 
};

Невозможно случайно преобразовать Data в string. Тип данных darget bool является исключением: в некоторых случаях допускается неявное преобразование, даже если оно помечено как explicit -- контекстное преобразование. Итак, вы можете использовать эти типы данных в if(...), например:

struct Ok {
    explicit operator bool(); // allowed in if(...) anyway
};

Параграф "25.4.(2) Сортировка и связанные операции", по-видимому, допускает это и для функтора Compare стандартных контейнеров, таких как set. Но мои попытки с gcc-4.7.0 терпят неудачу, и я уверен, что это мое недопонимание или ошибка в gcc?

#include <set>

struct YesNo { // Return value type of Comperator
    int val_;
    explicit YesNo(int y) : val_{y} {}
    /* explicit */ operator bool() { return val_!=0; }
};

static const YesNo yes{1};
static const YesNo no{0};

struct LessYesNo {  // Comperator with special return values
    YesNo operator()(int a, int b) const {
        return a<b ? yes : no;
    }
};

int main() {
    std::set<int,LessYesNo> data {2,3,4,1,2};
}

Без explicit перед operator bool() пример компилируется. И мое понимание «25.4.(2)» заключается в том, что это также должно компилироваться с `explicit.

Правильно ли я понял Std, что для set также должны работать преобразования explicit bool? И может тогда это баг в gcc, или я что-то не так понял?


person towi    schedule 04.11.2011    source источник
comment
Я согласен с вашим прочтением стандарта FWIW. Какую ошибку вы получаете от gcc?   -  person Alan Stokes    schedule 04.11.2011
comment
Я согласен с тем, что кажется, что это объяснено как таковое, поэтому оно может быть еще не реализовано в gcc (я опасаюсь говорить об ошибках, когда компиляторы все еще пишутся).   -  person Matthieu M.    schedule 04.11.2011


Ответы (2)


Мое прочтение стандарта немного отличается: в разделе 25.4 рассматриваются алгоритмы сортировки, а не отсортированные контейнеры; контекст, установленный в 25.4.(1), означает, что свойство объекта сравнения, указанное в 25.4.(2), применяется к алгоритмам в 25.4, а не к отсортированным контейнерам.

1 Все операции в 25.4 имеют две версии: одна принимает функциональный объект типа Compare, а другая использует оператор.

2 Compare — это тип объекта функции (20.8). Возвращаемое значение операции вызова функции, примененной к объекту типа Compare, при контекстном преобразовании в bool (4) дает значение true, если первый аргумент вызова меньше второго, и false в противном случае. Compare comp используется повсюду для алгоритмов, предполагающих отношение порядка. Предполагается, что comp не будет применять какие-либо непостоянные функции через разыменованный итератор.

Я не знаю, должен ли ваш пример работать или нет, но я не думаю, что раздел 25.4 здесь применим.

Быстрый тест с вектором и std::sort работает:

#include <list>
#include <algorithm>

struct YesNo { // Return value type of Comperator
    int val_;
    explicit YesNo(int y) : val_{y} {}
    explicit operator bool() { return val_!=0; }
};

static const YesNo yes{1};
static const YesNo no{0};

struct LessYesNo {  // Comperator with special return values
    YesNo operator()(int a, int b) const {
        return a<b ? yes : no;
    }
};

int main() {
    std::vector<int> data {2,3,4,1,2};
    std::sort(std::begin(data), std::end(data), LessYesNo());
}

Редактировать:

Параметр Compare ассоциативного контейнера определяется в соответствии с разделом 25.4:

1 Ассоциативные контейнеры обеспечивают быстрый поиск данных на основе ключей. Библиотека предоставляет четыре основных типа ассоциативных контейнеров: множество, мультимножество, карта и мультикарта.

2 Каждый ассоциативный контейнер параметризуется на ключе и упорядочивающем отношении Compare, которое вызывает строгое слабое упорядочение (25.4) на элементах ключа. Кроме того, карты и мультикарты связывают произвольный тип T с Ключом. Объект типа Compare называется объектом сравнения контейнера.

и 23. насколько я понимаю, не имеет других условий для типа Compare, поэтому кажется разумным предположить, что тип, удовлетворяющий ограничениям 25.4, в равной степени применим.

person je4d    schedule 04.11.2011
comment
Спасибо за пример, который работает. Я надеюсь, что это справедливо и для контейнеров, иначе это может потребовать аварийного восстановления. - person Matthieu M.; 04.11.2011
comment
Да, я бы тоже на это надеялся - сейчас я просматриваю раздел 23, чтобы узнать, что там говорится об аргументе сравнения. Также спасибо за голосование, это был мой первый пост в stackoverflow :) - person je4d; 04.11.2011
comment
23.2.4/2: Каждый ассоциативный контейнер параметризуется на ключе и упорядочивающем отношении сравнения, которое вызывает строгое слабое упорядочение (25.4) на элементах ключа. - person Dennis Zickefoose; 04.11.2011
comment
@je4d: Отлично! Теперь, когда вы указали на это, я удивляюсь, как я мог неправильно прочитать это. Я предполагаю, что мой мозг опередил меня, используя неявное знание о том, что наборы и карты хранятся отсортированными, что, конечно, отличается от сортировки. Спасибо. - person towi; 05.11.2011
comment
@towi Ta для моего первого принятия :) Но обратите внимание на редактирование внизу и комментарий Денниса Зикефуза, в §23.2.4.(2) подразумевается, что те же ограничения применяются к типам Comparator для отсортированных контейнеров, поэтому в свете этого Я бы посчитал это ошибкой в ​​libstdc++v3. - person je4d; 05.11.2011

Правильно ли я понял Std, что для set также должны работать явные логические преобразования?

Это своего рода серая зона спецификации. Возвращаемое значение функции сравнения должно быть «конвертируемым в логическое». Но что это значит в свете explicit operator bool() неясно.

Например, использование сравнения std::set можно записать так:

CompFunc functor;
if(functor(input, currVal))
  ...

Или можно сделать так:

CompFunc functor;
bool test = functor(input, currVal);
if(test)
  ...

Являются ли они оба технически законными в C++ 11? Без понятия. Очевидно, что второй терпит неудачу, если operator bool() равно explicit.

Я посмотрел определение std::shared_ptr, и там тоже есть explicit operator bool(). В разделе 20.7.2.2, параграф 2, также говорится, что std::shared_ptr "конвертируется в bool".

Поэтому я предполагаю, что вторая версия должна быть реализована следующим образом:

CompFunc functor;
bool test = static_cast<bool>(functor(input, currVal));
if(test)
  ...

Тот факт, что это нигде не указано явно в спецификации, означает, что это должно быть подано как отчет о дефекте. Но это, вероятно, также следует зарегистрировать как ошибку GCC/libstdc++.

Лично я, в целях безопасности, не стал бы на это полагаться.


О контекстной конверсии

Раздел 4, пункт 3 гласит:

Говорят, что выражение e, встречающееся в таком контексте, контекстуально преобразуется в bool и является корректным тогда и только тогда, когда объявление bool t(e); корректно, для некоторой придуманной временной переменной t

Таким образом, операции, которые «контекстно преобразуются в bool», означают, что explicit operator bool() будет работать. Поскольку функтор «Compare» std::set должен подпадать под требования 25.4, а эти требования включают «контекстно преобразованное в bool», это выглядит как ошибка GCC/libstdc++.

Я бы все равно избегал этого, когда вы можете помочь.

person Nicol Bolas    schedule 04.11.2011
comment
Критическая часть цитаты при контекстном преобразовании в bool (4). Я думаю, что контекстно в Стандарте специально предназначено для компенсации преобразования explicit в bool. - person Matthieu M.; 04.11.2011
comment
@MatthieuM.: Смотрите мое дополнение. - person Nicol Bolas; 04.11.2011
comment
Считаете ли вы, что преобразование std::shared_ptr в bool относится к контекстному преобразованию? Или shared_ptr<X> x ... ; bool b = (bool)x; также может быть преобразовано в bool? - person towi; 05.11.2011