Подтвердить что ты не робот

Несогласованное использование спецификатора const между объявлением и определением

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

Я также заметил, что поведение отличается от обычного и шаблонного классов. Также есть разница между тем, как он обрабатывается в GCC и Clang.

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

template <typename T> struct A {
    void f(const int);
};

template <typename T> void A<T>::f(int x) {
    x = 0;
}

struct B {
    void f(const int);
};

void B::f(int x) {
    x = 0;
}

void f() {
    A<float> a;
    a.f(0);

    B b;
    b.f(0);
}

Когда я компилирую с GCC, я не получаю ошибок. С Clang я получаю:

test.cpp:10:7: error: read-only variable is not assignable
    x = 0;
    ~ ^
test.cpp:26:7: note: in instantiation of member function 'A<float>::f' requested here
    a.f(0);
      ^

GCC предпочла классификатор при определении. Кланг использовал объявление и только для класса шаблона A.

Мои вопросы:

  • Является ли это регулируемым стандартом или эта реализация определена?
  • Почему поведение отличается от обычного и шаблонного классов?
  • Почему нет ошибки или, по крайней мере, предупреждения о том, что квалификатор const используется несогласованно между объявлением и определением?
  • Есть ли ситуация, когда это может быть полезно?

Update:

Согласно комментариям, это, по-видимому, ошибка Clang. Я открыл новый новый билет.

Update:

Исправлена ​​ошибка:

Исправлено в r203741

4b9b3361

Ответ 1

Это поведение определяется стандартом, и, насколько я могу судить, gcc здесь верен, если мы посмотрим на проект стандарта С++ section 13.1 Перегружаемые объявления в абзаце 3 гласят:

[...] - объявления параметров, которые отличаются только наличием или отсутствием константы и/или неустойчивой, эквивалентны. То есть, константные и неустойчивые типы-спецификаторы для каждого типа параметра игнорируются при определении того, какая функция объявляется, определяется или вызывается.

и предоставляет этот пример:

[ Example:
    typedef const int cInt;

    int f (int);
    int f (const int); // redeclaration of f(int)
    int f (int) { /* ... */ } // definition of f(int)
    int f (cInt) { /* ... */ } // error: redefinition of f(int)
—end example ]

и некоторые детали, поясняющие, что это относится только к самым отдаленным cv-классификаторам (акцент мой):

Только константные и неустойчивые типы-спецификаторы на самом внешнем уровне спецификации типа параметра игнорируются таким образом; const и volatile спецификаторы типов, закодированные в спецификации типа параметра, являются значительными и могут использоваться для различения описаний перегруженных функций. 123 В частности, для любого типа T, "указатель на T," "указатель на const T" и "указатель на volatile T" считаются отдельными типами параметров, как "ссылка на T", "ссылка на const T" и "ссылка на volatile T."

и, насколько я могу судить, это относится к функциям шаблона в классах шаблонов, а также из раздела 14.8 Специально специализированные специализированные шаблоны 14.8.3 Разрешение перегрузки, которое гласит:

[...] Полный набор функций-кандидатов включает в себя все синтезированные объявления и все перегруженные функции без шаблона с тем же именем. Синтезированные объявления обрабатываются как любые другие функции в остальной части разрешения перегрузки, за исключением случаев, явно указанных в 13.3.3. 144

Ответ 2

Это ошибка, потому что она предотвращает использование законного кода:

/* API declaration */
void f(int);


/* Implementation */
void f(const int x) /* my business: x is my local var and I want it const */
{
}

Я не могу поверить, что кто-то будет вводить код из своего пути, чтобы диагностировать это как проблему.

Кстати, GCC, который не жалуется на это, имел предупреждение об этой ситуации. Возможно, он все еще делает:

void f(int func_ptr(void));

void f(int (*func_ptr)(void))
{
}

Это чисто стилистическая несогласованность, которая не служит цели.