В последнее время я столкнулся с проблемой, которая как-то (но только как-то) имеет для меня смысл. Он основан на интерпретации конструкции временного объекта как объявления единственного (!) Аргумента конструктора. Пожалуйста, посмотрите на минимальный пример ниже.
#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);
Интерпретируются (если есть стандартный конструктор) как объявление. Это имеет смысл, и это совершенно нормально, так как вы можете просто использовать скобки {}, чтобы сделать конструкцию явной. Но то, что я не понимаю, это:
Почему существует проблема со строительством bar0?
Все Foo
У них нет стандартного конструктора. Так что нет смысла что-то толковать Foo0(x)
как декларация x
,
Почему строительство bar1
а также bar2
Работа?
Для меня очевидно, что строительство bar4
работает, так как я использую скобки {} для всех временных Foo
с, таким образом, я явно о том, что я хочу.
Если необходимо использовать скобки {} только с одним из Foo
S, чтобы решить проблему … почему строительство bar3
потерпеть поражение?
Кроме того, x объявляется перед созданием любого бара. Почему компилятор не жалуется на это?
Последний вопрос связан с моей последней строкой примера кода. Короче говоря: что, по мнению компилятора, я хочу, чтобы он делал, и где мне не хватает появления теней?
PS: если это интересно — я использую gcc-4.9.2.
PPS: я попробовал то же самое с bar
конструктор брал три Foo0
в качестве аргументов. Та же история здесь. Но ошибка ничего не говорит о конфликтующей декларации, а о переопределении x
,
Правило состоит в том, что если объявление имеет синтаксис объявления функции, то оно одно; в противном случае это объявление переменной. Удивительные случаи этого иногда называют наиболее раздражающий-синтаксический анализ.
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
что недопустимо — имена параметров функции должны быть разными. Если вы измените x
x
x
в x
y
z
ошибка уходит).
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
; у него нет никаких методов.
1) Foo0 (x) в этом случае рассматривается как параметр функции bar0. Здесь не имеет значения, имеет ли он стандартный конструктор или нет. Это не объявление и инициализация локальной переменной, а просто объявление параметра в объявлении функции.
2) Я предполагаю, что это как-то связано с анализом, но кто-то исправит меня, если я ошибаюсь. Примеры bar1 и bar2 работают, потому что компилятор знает, что bar1 и bar2 являются объявлениями локальных переменных (а не объявлениями функций) как как только он увидит первое вхождение {}. Эти первые вхождения {} появляются до того, как x объявлен дважды как параметр функции.
3) Конструкция bar3 не удалась, потому что компилятор сначала предполагает, что bar3 является объявлением функции. Объявление функции принимает три параметра, которые называются x. Очевидно, это не правильно.
4) Символ x в объявлении функции — это просто имя параметра. Он находится в другой области видимости, чем целое число x, которое вы объявили до этого.