Самый неприятный анализ: почему `g( ( f() ) );` не вызывает конструктор `f` по умолчанию и не передает результат в ctor `g`, который принимает `f`?

Это не дубликат самого неприятного синтаксического анализа: почему A a(()); work?, который основан на синтаксическом анализе в форме A a(());, чей OP считает, что он сможет построить объект A по умолчанию, используя дополнительный набор скобок.

Напротив, мой вопрос касается двух классов, f и g, где f имеет конструктор по умолчанию, а ctor g принимает f. Я хочу вызвать ctor g с временным аргументом f, и все это без использования единого синтаксиса инициализации. В ctor g есть оператор std::cout, поэтому отсутствие вывода означает объявление функции вместо создания экземпляра объекта g. Я аннотировал пример кода тремя цифрами в комментариях. №1 и №2 были скомпилированы с закомментированным №3, и наоборот:

#include <iostream>
struct f {};
struct g {
    g(f) { std::cout << "g's ctor\n"; }
};
int main() {
                          // -----Output-----
    g( f() );             // #1: function declaration; expected
    g( ( f() ) );         // #2: also a function declaration; UNEXPECTED
    // g myG( ( f() ) );  // #3: "g's ctor" ONLY if #1 & #2 are commented out;
                          //     ^ ... linker error otherwise
}

#1: я думал, что #1 объявляет анонимную функцию, которая возвращает g и принимает указатель на функцию, которая принимает 0 аргументов и возвращает f. Я ошибаюсь?

#2: Итак, я подумал, что дополнительный набор круглых скобок в #2 заставит оцениваться заключенное в нем содержимое как вызов функции, а именно вызов ctor по умолчанию f. Но это все еще объявление функции. Почему?

№3: — это вариант №2, отличающийся тем, что в №3 добавлено имя экземпляра myG. #3 создает экземпляр объекта, если #1 и #2 закомментированы. В противном случае я получаю эти ошибки в VC12:

error LNK2019: unresolved external symbol "struct g __cdecl f(void)" (?f@@YA?AUg@@XZ) referenced in function _main и

fatal error LNK1120: 1 unresolved externals.

и эта ошибка в g++ 4.8: undefined reference to 'f()'

Что они означают и почему я их получаю?

Почему # 3 является экземпляром объекта только тогда, когда экземпляр назван?

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


person CodeBricks    schedule 18.12.2013    source источник
comment
Ваш третий decl испорчен вашими первыми двумя (любыми), которые локально объявили g как функцию. поэтому это не может быть переменная g, как вы хотите в третьем случае (а также объясняет, почему она компилируется, когда исключаются и #1, и #2). Однако второй (№ 2) все еще вызывает у меня недоумение. Должен смириться с этим на минутку.   -  person WhozCraig    schedule 18.12.2013


Ответы (1)


Первый объявляет функцию с именем f, которая не принимает параметров и возвращает g.

     g( f() );
  //  ^     ^  redundant set of parentheses

Второй такой же, но с еще одним избыточным набором скобок (помните, что вы можете иметь столько объявлений одних и тех же функций, сколько пожелаете). Однако они не всегда бесполезны. Они нужны вам, скажем, для объявления функции, возвращающей указатель на функцию:

// function taking an int and returning
// a pointer to a function that takes a char
// and returns a g
g ( *f(int) )(char);
//^         ^ needed, syntax error without them

Что касается третьего:

Когда есть #1 и #2, у вас есть объявление функции для f в main, а g myG( ( f() ) ); анализируется как объявление объекта типа g с именем myG и инициализируется результатом вызова функции. Вы получаете ошибку компоновщика, потому что нет определения для f.

Когда # 1 и # 2 закомментированы, тип f виден, и срабатывает устранение неоднозначности с помощью круглых скобок:

g myG( ( f() ) )
//     ^     ^   these force an expression

Без этой пары вы получили бы еще одно объявление функции.

Что вы хотите, так это:

   ( g(f()) );
// ^        ^  must be an expression as declarations can't be parenthesized

или что-то менее Lisp-y: static_cast<g>(f());

person jrok    schedule 18.12.2013
comment
Черт, я забыл, что вы можете иметь несколько объявлений функций без ошибки компилятора. Спасибо, так и есть. - person chris; 18.12.2013
comment
@chris Это стоит упомянуть в ответе, спасибо. - person jrok; 18.12.2013