2015-09-24 4 views
0

Когда я читал о most vexing parse в C++, я еще больше смутился о разных интерпретациях выражения формы a_type_name(args) (args не является обязательным). Есть, по крайней мере, следующие во многих случаях использование:Явные вызовы конструкторов без декларатора

  1. Создание неназванный временного типа a_type_name. Например:

    Widget w = Widget("a simple widget"); // explicitly calling Widget constructor taking a string 
    f = processWidget(Widget()); // pass a default constructed Widget to function processWidget 
    

    Я никогда не понимаю, почему эта форма выражения действительна, за исключением того, что она воспринимается как есть. В конце концов, можете ли вы сделать что-то вроде int(), чтобы определить (или объявить?) Неназванное целое число? Или double(3.14) для инициализации неназванного двойника? Я не могу найти в моей книге C++ Primer любое формальное определение такого выражения --- Widget(), где отсутствует декларатор. Может ли кто-нибудь указать мне на официальное объяснение?

  2. Неназванный указатель функции

    Как объяснен в most vexing parse, когда Widget() может объявить неназванную функцию (указатель?), Что ничего не берет и возвращает виджет. Если это верно, то как тип C++ вывести следующее выражение:

    auto w = Widget(); // is w of type Widget Or a function pointer where the function takes no parameter and returns a Widget? 
    
    Может

    кто-то список все контекст, в котором выражение вида Widget() означает функцию (указатель)?

  3. Функциональная форма C-стиле литой

    Если есть путь преобразования, либо путем преобразования конструкторов или операторов преобразования или некоторые другие предопределенные правила преобразования неявные, выражение вида a_type_name(one_arg) может означать явное преобразование типов:

    Widget("convert string to widget") // a form of explicit cast 
    

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

+0

Это все тот же случай. Выражение 'Type (stuff)' означает: создать объект типа 'Type' с инициализатором' stuff'. –

ответ

1
A a(10); 

Не может быть проблемой. Он всегда будет на объекте типа A, построенном с аргументом 10.

A a(int); 

Не может быть проблемой. Это всегда будет функция, которая принимает int и возвращает A.

A a(); 

подвергается наиболее неприятному анализу.

Расширьте это, чтобы нечто более сложное.

A a(B b(), C c()); 

подвергается наиболее неприятному анализу.

A a(B(10), c(30.2f)); 

не подлежит обсуждению.

A a(B b(int), C c(float)); 

не подлежит обсуждению.

+0

Можете ли вы объяснить, что вы подразумеваете под «предметом самого досадного разбора»? –

+1

Это означает, что интерпретация кода неоднозначна из-за MVP в этом случае. –

0

На точке 1. Предполагая, что виджет является структурой или классом,

f = processWidget(Widget()); 

Для того чтобы работы, processWidget должны быть объявлены что-то вроде:

int processWidget(const & Widget); 

Хотя это может быть немного усложнено тем факт, что могут быть классы, полученные из Widget, которые могут автоматически конвертироваться в виджет или в другие контексты, в которых такое преобразование предоставляется, давайте просто рассмотрим эту базовую форму. Widget() собирается создать виджет «на месте». В других контекстах мы могли бы написать

Widget w(); 

что в точности то же самое, только теперь созданный виджет имеет имя (ш). Widget() собирается создать временное, которое будет уничтожено сразу же после завершения инструкции, в которой она отображается. Таким образом, этот временный виджет предоставляется как const & Widget функции processWidget. Это будет продолжаться до тех пор, пока эта функция будет выполняться. Когда эта функция вернется, она предоставит возвращаемое значение f, после чего временный виджет будет уничтожен (будет вызываться функция деструктора).

Заметим также, что вы можете сделать:

Widget w; 
w = Widget(); 

Это странное использование создает временный виджет, который копируется в ш. Причина, по которой это применимо к вашему вопросу о processWidget(Widget()), состоит в том, что = в этом контексте является оператором. Это может быть перегруженная операторская функция, написанная для предоставления некоторой уникальной операции копирования, но даже автоматически сгенерированная версия принимает параметр, const & Widget, о котором я упоминал для параметра в объявлении processWidget. Это означает, что в принципе это одно и то же.

В этой связи int() делает что-то подобное, но эта точная форма создавала бы int uninitialized в некоторых компиляторах, что не очень хорошо (в современном C++ оно было бы нулевой инициализировано).

Однако, если виджет() является функцией возвращает значение, объявленный как:

int Widget(); 

Затем processWidget должен был бы быть объявлена ​​таким образом, чтобы принять целое число, или тип, который преобразует на целое число, чтобы вызов работал.

В точке 2:

Widget() только будет «видеть» как тип функции, если есть объявление функции. Если Widget является структурой или классом, компилятор определяет это путем поиска. Когда компилятор пытается понять, что имеется в виду при использовании Widget, за которым следует распознаваемый оператор функции (), он тогда «видит», было объявление для функции этого имени, которое затем дает ему знание возвращаемого типа, параметры объявлен и т.д.Другими словами, Widget() как выражение не имеет изолированного значения для C++, не являющегося ключевым словом или другим распознаваемым объявлением, и ему предоставляется контекст, из которого значение выводится на основе предоставленного объявления. Если Widget объявлен как структура, компилятор не будет «знать» Widget как объявление функции, но может понимать, что он является структурой, для которой был предоставлен (или будет предоставлен) автоматический конструктор по умолчанию или для которого была дана соответствующая декларация соответствия.

Как указать 3.

Конструкторы понимается уникальная категория функций C++. Любой конструктор, принимающий единственный аргумент, подразумевает, что предоставленный параметр является способом построения объекта из других заданных типов. В вашем вопросе комментарий, который вы предоставили с

Widget("convert string to widget") // a form of explicit cast 

Предлагает это отливка, но это не так. Это конверсия. Это огромная разница. Приведение не создает новый объект, но выполняется преобразование.

Предполагается, что конструкторы, принимающие один параметр, являются объявлениями конверсии, они указывают, как создать виджет, заданный другим типом. Если виджет может быть изготовлен из const char *, как вы намекнули, это означает, что один конструктор в классе виджета объявить:

Widget(const char *); 

Это означает, что вы можете сделать виджет из строкового литерала, или что-то который преобразуется в строковый литерал (const char *). В контексте, где виджет может потребоваться в качестве параметра, он сообщает компилятору, что он может его построить, если предоставляется const char *. Это полная противоположность операторам преобразования, где они возвращают представление Widget, как если бы это был какой-то другой тип. Если, например, вы можете представлять Widget как std :: string, вы можете предоставить оператор преобразования для одного (хотя такие операторы могут быть сложными). Довольно типичное использование заключается в предоставлении оператора преобразования для bool. Это не означает, что Widget может представлять собой как bool, но если тип виджета использовался в контексте Bool, скажем, if (!w), где w имеет вид Widget, то этот виджетов ACTS, как bool в этот момент, возможно что виджет не подходит, и что-то нужно сделать.

+0

«Приведение не создает новый объект, но делает преобразование». - это неверно. 'T (x)' и '(T) x' эквивалентны. Результатом типа cast to object является временный объект. –

+0

Не 'Widget w();' пример досадного разбора, о котором спрашивает Rich. –

Смежные вопросы