Void(), оператор запятой (оператор) и невозможная (?) Перегрузка
Рассмотрим следующую структуру:
struct S {};
В С++ 14 верно следующее определение:
constexpr auto f() { return S{}, 'c'; }
Кроме следующего:
constexpr auto f() { return S{}, void(); }
Теперь рассмотрим следующий рабочий фрагмент, который включает в себя первое из двух определений:
#include<type_traits>
struct S {};
constexpr int operator,(S, char) { return 42; }
constexpr auto f() { return S{}, 'c'; }
int main() {
constexpr int i{f()};
static_assert(i == 42, "!");
static_assert(std::is_same<decltype(f()), int>::value, "!");
}
Говоря не так технически, перегрузка оператора запятой перехватывает пару S{}, 'c'
и возвращает целое число, которое правильно проверено в функции main
.
Теперь предположим, что я хочу сделать то же самое со вторым определением f
:
constexpr auto f() { return S{}, void(); }
В этом случае оператор запятой должен перехватить форму S{}, void()
.
Ни одно из следующих определений не работает (по понятным причинам):
constexpr int operator,(S, void) { return 42; }
И ни один ниже (который бы работал в предыдущем случае):
template<typename T> constexpr int operator,(S, T &&) { return 42; }
Есть ли способ перегрузить оператор запятой, чтобы иметь дело с S{}, void()
?
Разве это не недостаток в стандарте, так как он позволяет использовать оператор запятой таким образом, но не дает вам возможности перегружать тот же оператор (даже если стандартный что перегруженные функции с участием S
разрешены)?
Примечание: этот вопрос сделан ради любопытства. Пожалуйста, избегайте комментариев, например, не делайте этого, или это не очень хорошая практика. Я не планирую делать это в производственных условиях. Спасибо.
Ответы
Ответ 1
Соответствующее предложение для этого - 13.3.1.2/9 [over.match.oper] в N4140:
Если оператор является оператором ,
, унарный оператор &
или оператор ->
, и нет жизнеспособных функций, то оператор считается встроенным оператором и интерпретируется в соответствии с пунктом 5.
Поскольку void()
никогда не является допустимым аргументом функции (см. 5.2.2/7 [expr.call]), никогда не будет жизнеспособной функции, и поэтому будет использоваться встроенный ,
.
Так что нет, то, что вы пытаетесь сделать, невозможно.
Фактически, запись цикла итератора, подобного этому
for(...; ++it1, (void)++it2)
является стандартным способом предотвращения нарушения пользователем кода путем перегрузки ,
для их типов итераторов путем принудительного использования встроенного оператора ,
. (Обратите внимание, что я не говорю, что вам нужно сделать это в своем повседневном коде. Это очень зависит от его фактического использования. Это стандартный уровень библиотеки параноидального кода.)
Относительно стандартного предложения, которое вы указали:
Значение операторов =, (унарный) &, и (comma), предопределенных для каждого типа, может быть изменено для определенных классов и типов перечисления путем определения операторных функций, реализующих эти операторы.
Но такая функция не может определяться, потому что, как я сказал выше, void()
никогда не является допустимым аргументом функции.
В настоящее время обсуждается вопрос о том, является ли это надзором/проблемой в стандарте.