Странная нулевая инициализация с g ++ - PullRequest
20 голосов
/ 28 апреля 2011

Я столкнулся со странным поведением следующего кода, играя с инициализацией целых с использованием g ++ 4.4.3.

  int main()

    {

        int x(int());

        int y = int();

        cout << x << "  " << y <<  endl;
    }

результат:

1 0

Значение «y» равно 0, как и ожидалось, но значение x странно равно «1»!

На VS2008 выдает следующую ошибку ссылки (объявление функции, но без определения):

unresolved external symbol "int __cdecl x(int (__cdecl*)(void))"

Может кто-нибудь объяснить это странное поведение g ++?

Ответы [ 6 ]

24 голосов
/ 28 апреля 2011

int x(int()); анализируется как объявление функции.

Он объявляет функцию с именем x, возвращающую int и принимающую один параметр, который имеет тип функции, возвращающей int ине принимать аргументов.

Это известно как самый неприятный анализ.

13 голосов
/ 28 апреля 2011

В дополнение к ответу GMan здесь (x - это определение функции), почему 1.

Причина вывода 1 состоит в том, что в месте вызова std::cout << x функция превращается в указатель на функцию (язык не позволяет передавать функции в качестве аргументов другим функциям, поэтому как и для массивов, выполняется неявное преобразование в указатель на ). Теперь нет перегрузки для ostream, который принимает указатель на функцию, и компилятор пытается выбрать преобразование в любую из доступных перегрузок. В этот момент он обнаруживает, что последовательность преобразования best соответствует bool, и печатает 1 (указатель не равен 0).

Вы можете проверить это, изменив поведение, вы можете использовать std::cout << std::boolalpha << x, и это напечатает true вместо 1. Кроме того, интересно отметить, что VS правильно с этим, так как выражение std::cout << x требует принятия адреса x, тогда функция используется и программа плохо сформирована, если есть нет определения для этой функции. Вы можете снова проверить это, предоставив определение:

int f() {}
int main() {
   int x(int());      // 1
   x( &f );           // 2
}
int x( int(*)() ) {   // 3
   std::cout << "In x" << std::endl;
}

Где я вручную выполнил преобразование из function в pointer-to-function в определении x (1) и вызов с аргументом f (2) - обратите внимание, что объявление в 1 и определения в 3 имеют одинаковую подпись, и что & в x( &f ) будет выполняться компилятором, если вы этого не сделаете.

4 голосов
/ 28 апреля 2011

Просто добавьте еще парень:

int main()

    {

        int x((int()));

        int y = int();

        cout << x << "  " << y <<  endl;
    }

Теперь x - это int, а не функция.

3 голосов
/ 28 апреля 2011

Как уже говорили другие, x - это объявление функции. Поскольку для типов указателей на функции не определены предустановленные ostream-вставки, g ++ использует неявное преобразование bool (используется для проверки, имеет ли указатель функции NULL), чтобы найти способ его вывода.

Visual C ++, с другой стороны, жалуется на то, что объявленная функция x никогда не определяется, и поэтому она не может завершить ссылку. Я подозреваю, что g ++ в этом случае достаточно умен, чтобы видеть, что функция никогда не вызывается, и поэтому не беспокоится о ссылке.

Вы можете попробовать добавить фиктивное определение функции int x(int(*)()) { return 0xdeadbeef; } к коду и посмотреть, что тогда делает с ним MSVC.

2 голосов
/ 28 апреля 2011

C ++ интерпретирует первое как объявление функции.

1 голос
/ 28 апреля 2011

Это

int x(int());

На самом деле является объявлением функции, вход является функцией следующей подписи

int fn(void)

Указатель функции, переданный в std::cout <<, конвертируется в bool), так как это ненулевой указатель.

...