Void(), оператор запятой (оператор) и невозможная (?) Перегрузка

141
9

Рассмотрим следующую структуру:


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 разрешены)?


Примечание: этот вопрос сделан ради любопытства. Пожалуйста, избегайте комментариев, например, не делайте этого, или это не очень хорошая практика. Я не планирую делать это в производственных условиях. Спасибо.

спросил(а) 2016-09-15T18:19:00+03:00 3 года, 6 месяцев назад
1
Решение
162

Соответствующее предложение для этого - 13.3.1.2/9 [over.match.oper] в N4140:


Если оператор является оператором ,, унарный оператор & или оператор ->, и нет жизнеспособных функций, то оператор считается встроенным оператором и интерпретируется в соответствии с пунктом 5.



Поскольку void() никогда не является допустимым аргументом функции (см. 5.2.2/7 [expr.call]), никогда не будет жизнеспособной функции, и поэтому будет использоваться встроенный ,.


Так что нет, то, что вы пытаетесь сделать, невозможно.


Фактически, запись цикла итератора, подобного этому


for(...; ++it1, (void)++it2)

является стандартным способом предотвращения нарушения пользователем кода путем перегрузки , для их типов итераторов путем принудительного использования встроенного оператора ,. (Обратите внимание, что я не говорю, что вам нужно сделать это в своем повседневном коде. Это очень зависит от его фактического использования. Это стандартный уровень библиотеки параноидального кода.)


Относительно стандартного предложения, которое вы указали:


Значение операторов =, (унарный) &, и (comma), предопределенных для каждого типа, может быть изменено для определенных классов и типов перечисления путем определения операторных функций, реализующих эти операторы.



Но такая функция не может определяться, потому что, как я сказал выше, void() никогда не является допустимым аргументом функции.


В настоящее время обсуждается вопрос о том, является ли это надзором/проблемой в стандарте.

ответил(а) 2016-09-15T18:43:00+03:00 3 года, 6 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема