Скачать MetaTrader 5
Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий
Задействуй с пользой свободные компьютеры. Подключись к MQL5 Cloud Network!
Vadim Zhunko
5216
Vadim Zhunko 2014.08.17 22:50  
simpleton:

1. Да, это так, но ничего смешного в этом не вижу. Правда, последнее время они начали исправляться, что радует: C++11/14 Core Language Features in VS 2013 and the Nov 2013 CTP.

2. Хоть какие-нибудь аргументы можно увидеть, кроме "так всегда было"?
И заодно объяснить следующее явление, нагло имеющее место не только в C++, но даже в MQL4++:

Которое имеет наглость ещё и работать, как ожидается:

Как хорошо видно, в конструкции "int i = 7, j = i + 1;" для инициализации переменной j в выражении используется даже не адрес, а значение переменной i, хотя до "завершения оператора", то есть, до ";", дело ещё не дошло. Как это сопоставить с "так всегда было"?

Что ж, наверное следует обратиться к первоисточнику и, без преувеличения, - истине в последней инстанции, то есть, к стандарту C++11 (использован текущий драфт N3690):

Совершенно ясно сказано, что точка декларации находится сразу после полного декларатора и до его инициализации, то есть, в моём примере, - на открывающей круглой скобке после имени переменной, как я и говорил. Более того, в только что процитированной мной части стандарта приведён пример, очень близкий к нашему случаю, (во внутреннем блоке, образованном { }) и далее прямо написано, что для этого случая переменная инициализируется своим собственным же (неопределённым) значением. Если уж прямо в стандарте пример приведён, - тут уж никуда не деться...

3. Это не у меня в примере "не прокатило", а утверждение stringo оказалось неверным, мой пример лишь был специально так составлен, чтобы это показать.

4. Хотелось бы ясно увидеть, как из того, что пример компилируется, следует, что конструктор есть. И ссылку на хоть какой-нибудь вменяемый документ, где утверждается, что в C++ все базовые типы имеют конструкторы.

5. gcc version 4.7.2. Не настолько и устаревший.

Нет там никакого присваивания, там - инициализация, это сильно разные вещи. Более того, в сообщении об ошибках компилятор об этом прямым текстом и заявляет, цитирую: "error C2440: инициализация:". Инициализация, а не "невозможное присваивание". В сообщениях об ошибках также сказано о том, что, цитирую: "невозможно преобразовать" и "требуется явное приведение". Ничего об обнаружении "невозможного присваивания" там не сказано. И про конструкторы ни слова. Зачем же выдумывать?

6. Я специально привёл пример, чтобы показать, что stringo ошибся. Да, суть в том, что в C++ указатель на тип данных, отличных от типа void, НЕ приводится НЕЯВНО к указателю на void. Вот, в обратную сторону - приводится НЕЯВНО, а так, как утверждал stringo - нет.

2. В Студии иначе не помню, чтобы было. Запятая, в этом случае, это перечисление операторов. По сути тоже самое, что ";".

3. Ваш пример некорректен. Там нет приведения типа (для вас явного приведения). Уже несколько раз написал это.

4. Даже не хочется отвечать на вопрос с очевидным ответом.

Конструкция типа:

int i(123);

говорит о том, что у типа int есть не только конструктор по умолчанию.

5. Конечно, Вам никто не будет в сообщении об ошибке рассказывать внутреннее устройство базового типа. Всё предельно разжёвано на самом высоком уровне.

Инициализация происходит в конструкторе путём присваивания. Компилятор не может понять, каким образом, указатель на тип int должен принять указатель на тип void. Наверняка, те кто писали компилятор, посчитали это возможной ошибкой программиста. Чтобы он ещё раз хорошенько подумал, зачем он так делает.

6. Ещё раз... :-(  Неявное преобразование это надежда на авось. Не все классы и даже базовые типы имеют нужные конструкторы, перегруженные операторы присваивания и другие операторы с нужным типом. Поэтому надо всегда делать явное приведение типа. Для меня вовсе не существует неявного приведения типа. Всегда делаю явное. Даже слова такие забыл - явное/неявное. Оно для меня просто - приведение типа.

Кстати, последнее предложение очень сложно для понимания. Не осилил :-) 

Да, суть в том, что в C++ указатель на тип данных, отличных от типа void, НЕ приводится НЕЯВНО к указателю на void. Вот, в обратную сторону - приводится НЕЯВНО, а так, как утверждал stringo - нет.
Может можно попроще как-нибудь?
Простак
315
Простак 2014.08.17 23:45  
Zhunko:

2. В Студии иначе не помню, чтобы было. Запятая, в этом случае, это перечисление операторов. По сути тоже самое, что ";".

3. Ваш пример некорректен. Там нет приведения типа (для вас явного приведения). Уже несколько раз написал это.

4. Даже не хочется отвечать на вопрос с очевидным ответом.

Конструкция типа:

говорит о том, что у типа int есть не только конструктор по умолчанию.

5. Конечно, Вам никто не будет в сообщении об ошибке рассказывать внутреннее устройство базового типа. Всё предельно разжёвано на самом высоком уровне.

Инициализация происходит в конструкторе путём присваивания. Компилятор не может понять, каким образом, указатель на тип int должен принять указатель на тип void. Наверняка, те кто писали компилятор, посчитали это возможной ошибкой программиста. Чтобы он ещё раз хорошенько подумал, зачем он так делает.

6. Ещё раз... :-(  Неявное преобразование это надежда на авось. Не все классы и даже базовые типы имеют нужные конструкторы и перегруженные операторы присваивания с нужным типом. Поэтому надо всегда делать явное приведение типа. Для меня вовсе не существует неявного приведения типа. Всегда делаю явное. Даже слова такие забыл - явное/неявное. Оно для меня просто - приведение типа.

2. Значит ли это, что стандарт C++ ошибается, а компилятор от Microsoft - нет, когда он отказывается компилировать конструкцию "void *p(&p);"?

4. Очевидно, дело не в "не хочется".

Конструкция

int i(123);

является всего лишь прямой инициализацией:

8.5 Initializers    [dcl.init]
...
16 The initialization that occurs in the forms
  T x(a);
  T x{a};
   as well as in new expressions (5.3.4), static_cast expressions (5.2.9), functional notation type conver-
sions (5.2.3), and base and member initializers (12.6.2) is called direct-initialization.

3, 4, 5. По поводу инициализации и приведения типов (в том же параграфе 8.5):

17 The semantics of initializers are as follows. The destination type is the type of the object or reference being
initialized and the source type is the type of the initializer expression. If the initializer is not a single (possibly
parenthesized) expression, the source type is not defined.
...
— Otherwise, the initial value of the object being initialized is the (possibly converted) value of the ini-
tializer expression. Standard conversions (Clause 4) will be used, if necessary, to convert the initializer
expression to the cv-unqualified version of the destination type; no user-defined conversions are con-
sidered. If the conversion cannot be done, the initialization is ill-formed.

Ничего не сказано, что инициализация - это присваивание. Начальное значение инициализируемого объекта есть значение инициализирующего выражения, и - всё.

Зато сказано, что если исходный и конечный типы не совпадают, то используются стандартные преобразования (приведения). Именно поэтому компиляторы пытаются выполнить приведение типов (строго по стандарту, кстати) и ведут речь в сообщениях об ошибках о приведении типов (conversion).

Также сказано, что если приведения типов не могут быть выполнены, инициализация "неправильно оформлена". В нашем случае не может быть выполнено приведение от 'void *' к 'int *', и поэтому инициализация "неправильно оформлена", что приводит к ошибке компиляции.

6. Дело не в том, как кто-то относится к неявному преобразованию и кто как делает.

Дело в том, что в C++ отсутствует стандартное преобразование от 'void *' к 'T *', где T - некий тип, отличный от void, а из высказывания stringo следовало, что такое преобразование есть.

Я специально составил пример, из которого стало бы ясно, что такого преобразования нет.

Надеюсь, в третий раз не будет про авось?

Попроще - можно. Стандартное преобразование от 'T *' к 'void *' в C++ - есть, а от 'void *' к 'T *' - нет. Здесь T - тип, отличный от void.

Кстати, первая конструкция "void *p(&p);" как раз это и показывает: здесь destination type - есть тип переменной, то есть, 'void *', а source type - есть 'void **', то есть, указатель на тип 'void *'. А 'void *' - есть тип, отличный от типа 'void'. И компилятор совершенно не возражает стандартно привести от 'void **' к 'void *'.

Если 'void **' и 'void *' - сложно, ну, можно завести сначала переменную типа 'int *', а потом использовать её для инициализации переменной типа 'void *'. Увидеть, что компилятор нисколько не возражает.

Потом попробовать в обратном порядке и убедиться, что теперь компилятор возражает.

Алексей Тарабанов
7222
Алексей Тарабанов 2014.08.17 23:53  
Да, Вы опасный человек. 
/ /1234
Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий