Как мне проверить, является ли тип контекстуально конвертируемым в bool?
Я хочу написать концепцию, описывающую объекты-функции, которые не только вызываются , но и, например,std::function
, иметь «пустое» состояние , которое можно проверить с помощью контекстного преобразования в .
Булева проверяемая концепция « только экспозиция» звучит так, как будто она может быть уместной, но, поскольку она написана в терминахconvertible_to
, требует, чтобы его аргумент был неявно конвертирован в bool:
template<class T> concept BooleanTestable = std::convertible_to<T, bool>;
static_assert(BooleanTestable<void(*)()>); // ok
static_assert(BooleanTestable<std::function<void()>>); // fails :(
Есть ли что-то в стандарте
<concepts>
библиотека, которая может помочь, или мне нужно самому написать require-expression, и если да, то какую форму я должен использовать? Определение контекстно преобразованного в
bool
выражается в виде декларации-оператора (т.е.
bool t(E);
), но require-expressions может проверять только выражения:
template<class T> concept ContextuallyConvertibleToBool = requires (T x) {
bool(x); // constructor cast?
static_cast<bool>(x); // or static_cast?
x ? void() : void(); // or something a little stranger?
};
Учитывая отнесение к категории, удобочитаемость и обслуживание, я бы предпочел использовать форму, имеющую прецедент.
1 ответ
Формулировкаcontextually convertible
применимо к выражениям, а не к типам.
Некоторые языковые конструкции требуют, чтобы выражение было преобразовано в логическое значение. Говорят, что выражение E, появляющееся в таком контексте, контекстуально преобразуется в bool и является корректным тогда и только тогда, когда объявление bool t(E); корректно для некоторой выдуманной временной переменной t ([dcl.init]).
Вот почему точное определение концепции зависит от вашего контекста и того, что именно вы хотите ограничить.
Ниже приведен пример типа, который проходитconstructible_from<bool, T>
test, но не компилируется:
struct S {
constexpr explicit operator bool() && noexcept {
return false;
}
};
void F() {
S s;
static_assert(std::constructible_from<bool, S>);
if (s) {
}
}
То, что мы, вероятно, хотим выше, этоstd::constructible_from<bool, S&>
. Но это также влияет на имя вашего концепта, потому что теперь вы проверяете не тот факт, что ваш тип является контекстуально конвертируемым, а может ли ваш тип использоваться в конкретном контексте. Например:
struct S1 {
constexpr explicit operator bool() && noexcept {
return false;
}
};
struct S2 {
constexpr explicit operator bool() noexcept {
return false;
}
};
template <typename T>
concept CanBeUsedWithF = std::constructible_from<bool, T&>;
template <CanBeUsedWithF T>
void F() {
T s;
if (s) {
}
}
F<S1>(); // constraints not satisfied
F<S2>();