Рассмотрим следующую структуру:
struct S {};
В C ++ 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
разрешены)?
Заметка: этот вопрос сделан ради любопытства. Пожалуйста, избегайте комментариев, таких как не делай этого или же это не очень хорошая практика. Я не планирую делать это в производственных условиях. Спасибо.
Соответствующее условие для этого — 13.3.1.2/9 [over.match.oper] в N4140:
Если оператор является оператором
,
, унарный оператор&
или оператор->
и нет жизнеспособных функций,
тогда оператор считается встроенным оператором и интерпретируется в соответствии с разделом 5.
Как void()
никогда не является допустимым аргументом функции (см. 5.2.2 / 7 [expr.call]), никогда не бывает жизнеспособной функции и, следовательно, встроенной ,
будет использоваться.
Так что нет, то, что вы пытаетесь сделать, невозможно.
На самом деле, написание цикла итератора, как это
for(...; ++it1, (void)++it2)
это стандартный способ предотвратить взлом вашего кода пользователями из-за перегрузки ,
для их типов итераторов, применяя встроенный оператор ,
использоваться. (Обратите внимание, что я не говорю, что вы должны делать это в своем повседневном коде. Это очень сильно зависит от его фактического использования. Это стандартный уровень библиотеки для параноиков.)
Относительно стандартного предложения вы связали:
Смысл операторов =, (унарный) &и (запятая), предварительно определенные для каждого типа, могут быть изменены для определенного класса и типов перечисления путем определения операторских функций, которые реализуют эти операторы.
Но такую функцию нельзя определить, потому что, как я сказал выше, void()
никогда не является допустимым аргументом функции.
Теперь, является ли это упущением / проблемой в стандарте, открыто для обсуждения.
Других решений пока нет …