2015-01-19 7 views
8

В последнее время я столкнулся с проблемой, которая каким-то образом (но только как-то) имеет смысл для меня. Он основан на интерпретации конструкции временного объявления как объявления единственного (!) Аргумента конструктора. Пожалуйста, ознакомьтесь с минимальным примером ниже.Построение временного вызова функции интерпретируется как объявление

#include <iostream> 

class Foo0{ 
public: 
    Foo0(int a){}; 
    void doStuff() {std::cout<<"maap"<<std::endl;}; 
}; 

class Foo1{ 
public: 
    Foo1(int a){}; 
    void doStuff() {std::cout<<"maap"<<std::endl;}; 
}; 

class Foo2{ 
public: 
    Foo2(int a){}; 
    void doStuff() {std::cout<<"maap"<<std::endl;}; 
}; 

class Bar{ 
public: 
    Bar(Foo0 foo0, Foo1 foo1, Foo2 foo2){}; 
}; 

int main() { 
    int x = 1; 

    Bar bar0(Foo0(x), Foo1(x), Foo2(x)); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’; conflicting declaration ‘Foo2 x’ previous declaration as ‘Foo0 x’ 
    Bar bar1(Foo0{x}, Foo1(x), Foo2(x)); // Works WTF 
    Bar bar2(Foo0(x), Foo1{x}, Foo2(x)); // Works WTF 
    Bar bar3(Foo0(x), Foo1(x), Foo2{x}); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’ 
    Bar bar4(Foo0{x}, Foo1{x}, Foo2{x}); // Works totally makes sens to me 

    x.doStuff(); //Dose not work. This makes sens to me. But in the context its curious though. 
} 

Я уже прочитал, что такие выражения, как:

Foo(a); 

интерпретируются (если есть стандартный конструктор) в объявлении. Это имеет смысл, и это совершенно нормально, поскольку вы можете просто использовать {} -бетограммы, чтобы сделать конструкцию явной. Но я не понимаю:

  1. Почему существует проблема со строительством bar0? Все Foo s не имеют стандартного конструктора. Поэтому не имеет смысла интерпретировать что-то вроде Foo0(x) как объявление x.

  2. Почему работает bar1 и bar2? Для меня очевидно, что работает конструкция bar4, так как я использую {} -бетоны для всех временных Foo s, поэтому я четко о том, что хочу.

  3. Если для решения проблемы необходимо использовать только {} -бетоны с одним из Foo s ... почему конструкция bar3 не работает?

  4. Кроме того, x объявляется до того, как будет построена любая панель. Почему компилятор не жалуется на это?

Последний вопрос связан с моей последней строкой примера кода. Короче говоря: что компилятор думает, что я хочу, чтобы он это сделал, и где мне не хватает видимости тени?

PS: Если это интересно, я использую gcc-4.9.2.
PPS: Я пробовал то же самое с конструктором bar с тремя аргументами Foo0. Та же история здесь. Но ошибка ничего не говорит о противоречивой декларации, а о переопределении x.

+1

Как вы знаете, 'bar0' является объявлением функции, которая принимает три' Foo 'и возвращает' Foo', она сама по себе не является объектом 'Foo'. Кроме того, тот факт, что объявление 'bar3' не работает, является ошибкой в ​​любом компиляторе, который вы используете. Эта строка должна работать так же, как в [clang] (http://coliru.stacked-crooked.com/a/d5f8305eff385830). – 0x499602D2

+0

@ 0x499602D2 прослушивается для меня в g ++ 4.8.1 –

+0

Верно: 'bar0' - это объявление функции, которая принимает три' Foo 'и возвращает' Bar'. – sedriel

ответ

10

Правило состоит в том, что если объявление имеет синтаксис объявления функции, то оно одно; в противном случае это объявление переменной. Удивительные примеры этого иногда называют .

Bar bar0(Foo0(x), Foo1(x), Foo2(x)); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’; conflicting declaration ‘Foo2 x’ previous declaration as ‘Foo0 x’ 

Это объявление функции: bar0 это имя, Bar является тип возвращаемого значения, а также типы параметров Foo0, Foo1 и Foo2. Имена параметров - все x, что является незаконным - имена параметров функции должны быть разными. Если вы измените xxx до xyz ошибка исчезнет).

Bar bar1(Foo0{x}, Foo1(x), Foo2(x)); // Works WTF 
Bar bar2(Foo0(x), Foo1{x}, Foo2(x)); // Works WTF 
Bar bar4(Foo0{x}, Foo1{x}, Foo2{x}); // Works totally makes sens to me 

Эти линии и создавать объекты bar1, bar2 и bar4 типа Bar. Они не могут быть проанализированы как объявления функций, потому что нотация { } не является допустимым синтаксисом в объявлении функции.

Следовательно, Foo0{x} и т. Д. Являются выражениями, которые предоставляют аргументы для конструктора Bar. Foo0{x} и Foo0(x) являются эквивалентными способами объявления временного типа Foo0 с инициализатором x.

Bar bar3(Foo0(x), Foo1(x), Foo2{x}); // Does not work: conflicting declaration ‘Foo1 x’ previous declaration as ‘Foo0 x’ 

Я думаю, что это ошибка компилятора; часть Foo2{x} означает, что эта строка не может быть объявлением функции; и он выглядит как действительное объявление переменной bar3.

x.doStuff(); //Dose not work. This makes sens to me. But in the context its curious 

x является int; у него нет никаких методов.

+0

Как bar0 объявляет юридическую функцию? Что означает 'type (name)' в этом контексте? Это просто привязка/группировка, например 'int (* foo)' - поэтому 'type (name)' совпадает с именем типа? Мне действительно нравилось время от времени на C++. – JasonN

+0

@JasonN [см. Здесь] (http://stackoverflow.com/questions/21624880/how-does-this-declaration-invoke-the-most-vexing-parse) для хорошего объяснения –

+0

Спасибо. Удивительность сложных анализов. '-Wvexing-parse' смешно. Грустно в то же время. – JasonN

2

1) Foo0 (x) рассматривается в качестве параметра функции bar0 в этом случае. Здесь не имеет значения, имеет ли он стандартный конструктор или нет. Это не объявление локальной локализации и инициализация, а просто объявление параметра в объявлении функции.

2) Мое предположение, что это имеет какое-то отношение к разбору, но кто-то поправьте меня, если я ошибаюсь. Примеры bar1 и bar2 работают, потому что компилятор знает, что bar1 и bar2 являются локальными объявлениями переменных (а не объявления функций), как только он видит первое вхождение {}. Эти первые вхождения {} появляются до того, как x объявлен дважды как параметр функции.

3) Конструкция bar3 терпит неудачу, потому что компилятор сначала предполагает, что bar3 является объявлением функции. Объявление функции принимает три параметра, все они называются x. Очевидно, это неверно.

4) Х в объявлении функции - это просто имя для параметра. Он отличается от целого x, который вы объявили перед этим.