Вариантность компилятора для перегрузки ссылочных параметров массива

Следующая программа, как и ожидалось, принимается как GCC, Clang, так и MSVC (для различных версий компилятора и языка):

// #g.1: rvalue reference function parameter
constexpr bool g(int&&)       { return true; }

// #g.2: const lvalue reference function parameter
constexpr bool g(int const&)  { return false; }

static_assert(g(0), ""); // OK: picks #g.1

Аналогичный случай, но для перегрузок по ссылочному параметру множество {rvalue, const lvalue}, однако, отклоняется GCC, в то время как он принимается Clang и MSVC:

#include <cstddef>

// #f.1: rvalue ref overload
template<std::size_t size>
constexpr bool f(int (&&)[size])      { return true; }

// #f.2: lvalue ref overload
template<std::size_t size>
constexpr bool f(int const (&)[size]) { return false; }

static_assert(f({1, 2, 3}), ""); // Clang: OK    (picks #f.1)
                                 // MSVC:  OK    (picks #f.1)
                                 // GCC:   Error (picks #f.2)

ДЕМО.

На первый взгляд это похоже на ошибку GCC, но я хотел бы быть уверен.

Вопрос

  • Какой компилятор(ы) здесь?

@ user17732522 Вы правы, спасибо. Обновлены вопросы и ответы.

dfrib 18.03.2022 20:38

В вашем первом примере у вас есть const T&& вместо просто T&&. Обычно не имеет смысла иметь ссылку const rvalue, так как (1) T&&, но не const T&&, является пересылающей/универсальной ссылкой, и (2) фактическая ссылка rvalue на объект обычно используется, когда кто-то хочет взять ресурсы из этот объект и, таким образом, изменить его.

jjramsey 18.03.2022 20:44

@jjramsey Я знаю, я намеренно добавил const в первый пример, чтобы сделать его ссылкой на rvalue (отредактируйте на основе наблюдения user177.. выше: я ошибочно подумал, что #f.1 был ссылкой на пересылку; это был минимальный набор на телефоне исправить, чтобы изменить эталонный пример #g.1 на перегрузку rvalue).

dfrib 18.03.2022 20:59

@jjramsey Вернулся на компьютер, поэтому обновил вопросы и ответы, чтобы удалить ненужный параметр шаблона типа.

dfrib 21.03.2022 09:08
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
4
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

GCC ошибается

Поскольку аргумент является списком инициализаторов, применяются специальные правила согласно [over.ics.list]/1:

When an argument is an initializer list ([dcl.init.list]), it is not an expression and special rules apply for converting it to a parameter type.

Поскольку тип параметра для обеих перегрузок является ссылочным типом, [over.ics.list]/9 применяется [акцент мой]:

Otherwise, if the parameter is a reference, see [over.ics.ref].

[Note 2: The rules in this subclause will apply for initializing the underlying temporary for the reference. — end note]

Хотя это и ненормативно, это означает, что вызов

f({1, 2, 3})

для обеих функций-кандидатов (согласно [over.ics.list]/6), как если бы

template<typename T>
using type = T;

f(type<int[3]>{1, 2, 3});  // prvalue array

[over.ics.ref], в частности [over.ics.ref]/3, просто указывает на то, что этот тип аргумента prvalue может напрямую связываться как со ссылкой rvalue, так и со ссылкой const lvalue, что означает, что оба кандидата являются жизнеспособными кандидатами.

Переходим к [over.ics.ran]/3.2.3:

/3.2 Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if

  • [...]
  • /3.2.3 S1 and S2 include reference bindings ([dcl.init.ref]) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference

Таким образом, GCC ошибается, выбирая перегрузку ссылки const lvalue #f.2, поскольку #f.1 является наилучшей жизнеспособной функцией.


Мы можем заметить, что все компиляторы соглашаются с перегрузкой #f.1, если мы явно передаем массив rvalue вместо использования списка инициализаторов (ДЕМО):

template<typename T>
using type = T;

// All compilers agree: OK
static_assert(f(type<int[3]>{1, 2, 3}));

Таким образом, недопустимое поведение GCC в исходном примере, возможно, связано именно с тем, как GCC обрабатывает аргументы списка инициализаторов для функций-кандидатов, чей соответствующий тип параметра функции имеет тип ссылки на массив.


Сообщение об ошибке

  • Ошибка 104996 — Разрешение перегрузки по эталонным параметрам массива rvalue/const lvalue для инициализации. аргумент list неправильно выбирает константу lvalue ref. перегрузка

Возможное исправление: github.com/gcc-mirror/gcc/compare/master...ecatmur:pr-104996

ecatmur 21.03.2022 11:44

@ecatmur Хорошо! Является ли "Пожалуйста, проверьте и сообщите". запрос отчета об ошибке, предназначенный для других сопровождающих GCC или для меня как автора отчета об ошибке? (У меня нет опыта проверки исправлений GCC).

dfrib 21.03.2022 13:01

Да, надеюсь, у вас есть более сложный тестовый пример, с которым вы можете попробовать? (Ничего страшного, если нет, конечно). Сборка gcc в наши дни относительно проста; см. gcc.gnu.org/wiki/УстановкаGCC

ecatmur 21.03.2022 19:04

@ecatmur У меня нет более сложного варианта использования, чем пример отчета об ошибке (резюмированный Патриком), но я рад попробовать на выходных в качестве поучительного упражнения.

dfrib 22.03.2022 09:37

Другие вопросы по теме