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

Я играю с новым 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)" заключается в том, что это также должно компилировать с явным.

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

Ответ 1

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

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

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

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

Быстрый тест с использованием функции vector и 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());
}

Edit:

Ассоциативный контейнер. Параметр сравнения определяется в терминах secion 25.4:

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

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

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

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


По контекстуальному преобразованию

В пункте 4 раздела 4 говорится:

Выражение e, появляющееся в таком контексте, называется контекстно преобразованным в bool и хорошо сформировано тогда и только тогда, когда декларация bool t (e); хорошо сформирована, для некоторой изобретенной временной переменной t

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

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