Почему C ++ допускает, но игнорирует применение const к типам функций? - PullRequest
6 голосов
/ 07 декабря 2011

Я получаю удовольствие от изучения необычных уголков C ++. Узнав о реальных типах функций, а не об указателях функций из этого вопроса , я попытался возиться с типизацией функций и придумал такой странный случай:

typedef int Func(int);

int Foo(int x) { return 1; }

int main()
{
    const Func*& f = &Foo;

    return 0;
}

Поскольку &Foo является значением типа Func*, я решил, что смогу поместить его в константную ссылку, но я получаю эту ошибку из g ++ 4.6:

funcTypes.cpp: In function ‘int main()’:
funcTypes.cpp:7:23: error: invalid initialization of non-const reference of type ‘int (*&)(int)’ from an rvalue of type ‘int (*)(int)’

Но f - это const! Мне стало очевидно, что применение const к функции (или ссылка / ссылка на указатель и т. Д.) Просто игнорируется; этот код компилируется просто отлично:

template <typename A, typename B>
struct SameType;

template <typename A>
struct SameType<A, A> { };

typedef int Func(int);

int main()
{
    SameType<const Func, Func>();

    return 0;
}

Полагаю, это то, как boost использует их черту типа is_function, но мой вопрос - почему C ++ позволяет это игнорировать, а не запрещать?

РЕДАКТИРОВАТЬ: теперь я понимаю, что в первом примере f не является const и что const FuncPtr& f = &Foo работает. Однако это был просто фон, реальный вопрос - тот, что выше.

Ответы [ 6 ]

4 голосов
/ 07 декабря 2011

Но е постоянна!

Нет, это не так. Вы путаете

const Func*& f = &Foo;

с

Func* const& f = &Foo;

Первый является неконстантным указателем на константный указатель. Последний является константным указателем на неконстантный указатель.

Вот почему я всегда пишу константу перед * / &, а не перед типом. Я всегда писал бы первый случай как

Func const*& f = &Foo;

и затем прочитайте справа налево: ссылка на указатель на константную функцию.

3 голосов
/ 07 декабря 2011

В c ++ 03 это не было проигнорировано, но плохо проработано (и это был случай sfinae). Я предполагаю, что они изменили это в c ++ 11, потому что тогда вы можете просто иметь параметры функции, равные const F&, и передавать ей функциональные объекты, а также обычные функции.

См. Этот DR, который внес изменения http://www.open -std.org / jtc1 / sc22 / wg21 / docs / cwg_defects.html # 295

2 голосов
/ 07 декабря 2011

&Foo - указатель. В целом, я бы предложил избегать ссылок на указатели (const или нет). По крайней мере, если вы не знаете, что делаете.

Итак, вы должны иметь:

const Func *f = &Foo;

Или действительно, вы можете полностью отказаться от const:

Func *f = &Foo;

почему C ++ позволяет это игнорировать, а не запрещать?

Потому что вы говорите о двух разных вещах.

В C ++ существует разница между типом функции и функцией указатель . Foo - это тип функции, в частности int(int). &Foo - указатель на функцию типа int(*)(int). При необходимости тип функции разлагается на указатель функции (так же, как типы массивов разлагаются на указатели). Но они различны (как массивы).

Так что ваши два дела не совпадают. Ваш первый случай имеет дело с указателем на функцию, а второй - с типом функции (именно так выводится аргумент шаблона).

Что касается того, почему типы функций поглощают const, то это потому, что значения типов функций уже неявно постоянны. Вы не можете их изменить. Единственная операция, которую вы можете выполнить с типом функции - это () (или преобразование в указатель на функцию). Таким образом, const T эквивалентно T, если T является типом функции. Visual Studio 2010 фактически выдает предупреждение об этом.

1 голос
/ 07 декабря 2011

Следующие компиляторы в порядке:

typedef int Func(int);
int Foo(int x) { return 1; }

int main()
{
  Func* const& f = &Foo;  //ok
  return 0;
}

Помните, что операторы const оцениваются вместе с указателями и ссылками справа налево.Последняя константа слева от вас, которую вы написали, переводит на последнюю возможную позицию справа от имени ( C ++ FAQ по размещению констант ).Следовательно,

const Func*& f 

переводится компилятором в

Func const*& f 

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

0 голосов
/ 07 декабря 2011

Иногда сообщения об ошибках могут быть немного загадочными.

Я собрал пример на ideone , чтобы проиллюстрировать типы, напечатанные gcc для множества вещей:

#include <iostream>

typedef int(Func)(int);
typedef Func* FuncPtr;
typedef FuncPtr& FuncPtrRef;
typedef FuncPtr const& FuncPtrConstRef;

template <typename T> void DisplayType() { T::foo(); }

int main() {
  DisplayType<Func>();
  DisplayType<FuncPtr>();
  DisplayType<FuncPtrRef>();
  DisplayType<FuncPtrConstRef>();

}

Ошибки компиляции дают:

  • Func имеет тип int ()(int) (недопустимый тип, теперь должен быть исправлен в gcc)
  • FuncPtr относится к типу int (*)(int)
  • FuncPtrRef относится к типу int (*&)(int)
  • FuncPtrConstRef относится к типу int (* const&)(int)

В вашем сообщении об ошибке есть int (*&)(int), который является ссылкой на неконстантный указатель на тип функции.

Вам не разрешено привязывать r-значение к неконстантной ссылке.

Примечание: таким образом, это не имеет ничего общего с проглатыванием const, так как зажигает с правильно диагностированным

0 голосов
/ 07 декабря 2011

Нет, f не является постоянным. Прежде всего, это ссылка на некоторый изменяемый тип (этот изменчивый тип оказывается константным указателем, то есть изменяемым указателем на то, что вы обещаете не изменять через этот конкретный указатель). Однако с &Foo вы создаете временную (типа Func*) и не можете назначить временную для изменяемой ссылки. Вы можете назначить его только на постоянную ссылку. И это именно то, что сообщение об ошибке говорит вам.

...