Как определить преобразование типа класса в указатель на функцию? - PullRequest
2 голосов
/ 04 мая 2020

Я пытаюсь понять больше преобразования типов классов. Я читаю C ++ учебник 5ed. Итак, я попробовал этот код:

int add(int x, int y) { return x + y;}

struct Foo
{
    //operator(int(*)(int, int))(){return add;} // error
    using pfn = int(*)(int, int);
    operator pfn(){return add;} // OK
    double value = 5.45;
};

int main()
{

    cout << (int(*)(int, int))Foo()(5, 7) << endl; // why 1
    cout << ((int(*)(int, int))Foo())(5, 7) << endl; // Ok 12

    std::cout << "\nDone!\n";
}
  • Так почему я не могу напрямую определить преобразование для моего класса, используя тип int(*)(int, int), но я могу с псевдонимом типа?

  • Почему я получаю значение 1 в первом утверждении, которое является ошибочным, и получаю правильное значение во втором утверждении, используя круглые скобки?

  • Я получаю предупреждение из первого заявления: Description Resource Path Location Type cast to pointer from integer of different size [-Wint-to-pointer-cast] main.cpp /MyCppProj line 31 C/C++ Problem

1 Ответ

3 голосов
/ 04 мая 2020
  • Так почему я не могу напрямую определить преобразование для своего класса, используя тип int(*)(int, int), но могу определить псевдоним типа?

Грамматика для "operator TYPE" имени функции преобразования гораздо более ограничено, чем более общий декларатор или type-id . Он вообще не допускает скобок, только спецификатор типа (например, псевдоним типа, unsigned int, имя класса и т. Д. c.), Комбинации *, &, &&, const и volatile токены и [[ атрибуты ]]. Я не могу точно сказать, почему, но такие сложные заявления сложно написать, прочитать и проанализировать. Возможно, в некоторых случаях существует потенциальная неоднозначность, если разрешено больше, или, может быть, они просто не хотят требовать, чтобы компиляторы выяснили это.

Кроме того, если бы было разрешено, может быть форма будет operator int (*())(int, int);, а не operator (int(*)(int, int))()? Или, может быть, это тоже не имеет смысла. Видеть? Tricky.

  • Почему я получаю значение 1 в первом утверждении, которое является ошибочным, и получаю его правильно во втором утверждении с использованием скобок?

Синтаксис вызова функции имеет более высокий приоритет , чем C стиль приведения. Так что

(int(*)(int, int))Foo()(5, 7)    // (1)
(int(*)(int, int)) (Foo()(5, 7)) // (2), same as (1)
((int(*)(int, int))Foo()) (5, 7) // (3), not the same

Выражение (1) или (2) оценивает, сначала создав временный Foo. За ним следует синтаксис вызова функции, и Foo не определяет operator(), но C ++ также проверит, неявно ли он преобразуется в указатель или ссылку на функцию, и делает, так что (5, 7) выполняет неявное преобразование и вызывает результирующий указатель на add, давая 12. Это приведено к типу указателя функции, который имеет результаты, определенные реализацией. Для указателей на функции не объявлено operator<<, но есть для bool, и указатель на функцию может неявно преобразовываться в bool. Предположительно, результатом странного приведения было не нулевое значение указателя, поэтому конечный результат равен true, и вы видите значение 1. (Если вы сделали std::cout << std::boolalpha ранее, вы должны увидеть true или соответствующий перевод вместо этого.)

Одним из элементов этого, помимо недопонимания приоритета оператора, является опасность стиля C бросок, который может делать очень много разных вещей, некоторые обычно не предназначены. Вместо этого используйте static_cast<int(*)(int,int)>(Foo())(5,7), и все в порядке. Или, если мы случайно набрали static_cast<int(*)(int,int)>(Foo()(5,7)), компилятор выдает ошибку о преобразовании из int в int(*)(int,int), поскольку это может сделать только приведение в стиле reinterpret_cast или C.

  • Я получаю предупреждение от первого оператора: Description Resource Path Location Type cast to pointer from integer of different size [-Wint-to-pointer-cast] main.cpp /MyCppProj line 31 C/C++ Problem

Даже если приведение в стиле C заставляет преобразование из int в указатель на функцию, чтобы быть действительным, компилятор предупреждает, что int не имеет достаточно байтов для представления указателя на функцию. Предполагается, что значение int ранее было получено от приведения указателя функции к какому-либо типу цифр c, и это предназначено для обратного преобразования, но всякий раз, когда оно преобразовывалось из достаточно большого типа в int, значение указателя терялось. .

...