Согласно cppreference, начиная с C++23.
Для нестатической невиртуальной функции-члена, не объявленной с помощью cv-квалификатора или ref-квалификатора, ее первый параметр, если он не является пакетом параметров функции, может быть явным параметром объекта (обозначаемым префиксным ключевым словом
this
).
Может ли этот параметр иметь тип void
? Текущие компиляторы допускают это, но различаются в вызове такой функции-члена:
struct A {
void f(this void) {}
};
// ok everywhere
auto p = &A::f;
int main() {
// ok in GCC and MSVC
p();
// ok in Clang
A{}.f();
}
Кланг отклоняет p();
с помощью
ошибка: вызываемый тип объекта 'void (A::*)()' не является функцией или указателем на функцию
А GCC не любит A{}.f();
потому что
примечание: кандидат ожидает -1 аргументов, предоставлено 0
А также MSVC:
ошибка C2660: 'A::f': функция не принимает 0 аргументов
Онлайн-демо: https://gcc.godbolt.org/z/zMaqaTTav
Какой компилятор здесь прав?
На прошлой неделе был еще один похожий вопрос: stackoverflow.com/questions/78733013
Лол, «кандидат ожидает -1 аргументов, предоставлено 0»
Судя по тому, как написан стандарт в настоящее время, я бы склонен сказать, что именно Clang ведет себя правильно.
Согласно [dcl.fct]/3:
Список параметров, состоящий из одного безымянного параметра независимого типа void, эквивалентен пустому списку параметров.
Ключевое слово this
не влияет на тип параметра, который по-прежнему void
и остается безымянным. Поскольку он не находится внутри шаблона, он также тривиально не зависит.
Остальная часть [dcl.fct] предполагает, что эта корректировка объявления была сделана без повторного упоминания об этом явно, например, при определении типа функции в [dcl.fct]/5.
Поэтому я думаю, что [dcl.fct]/6, который определяет эффект this
в объявлении параметра для объявления явного параметра объекта, также следует учитывать после void
-корректировки.
Затем, поскольку объявление параметра this void
было удалено из списка, функция является не явной объектной функцией, а неявной объектной функцией без каких-либо параметров.
Тип функции будет void()
и &A::f
, тогда формируется указатель на член типа void (*A::f)()
. Вызов указателя члена с помощью синтаксиса вызова функции p()
невозможен.
Однако вызов нестатической функции-члена с выражением доступа к члену A{}.f()
возможен. this
в определении функции будет указывать на временный объект, материализованный из A{}
. Аргументы вызова не нужны, поскольку f
не имеет параметров.
Однако я не уверен, действительно ли это желательное поведение. Специальная настройка [dcl.fct]/3 существует только для обратной совместимости C. Нет необходимости расширять его до this void
, что в любом случае недопустимо в C. Вместо этого я думаю, что его следует сделать некорректным, как и все другие варианты использования (с учетом cv) void
параметров.
В любом случае поведение GCC и MSVC не имеет смысла. Даже если this void
по-прежнему приводит к тому, что функция является явной объектной функцией и не имеет неправильного формата, тогда, независимо от того, считается ли this void
фактическим параметром, остальные спецификации стандарта не будут согласованными. Либо это будет явная объектная функция без явного объектного параметра, который, однако, необходимо инициализировать при вызовах. Или это может быть функция с параметром void
, которую необходимо инициализировать, но объектов void
вообще не существует.
Это CWG2915, который предлагает сделать программу(void f(this void);
) некорректной.
От CWG2915:
Изменить пункт 3 9.3.4.6 [dcl.fct] следующим образом:
Если предложение объявления параметра пусто, функция не принимает аргументов. Список параметров, состоящий из одного безымянного необъектного параметра независимого типа
void
, эквивалентен пустому списку параметров. За исключением этого особого случая, параметр не должен иметь тип cv void.
(выделено мной)
Похожий вопрос, но про int, был задан несколько дней назад stackoverflow.com/questions/78739117/…