Не выдёргивается толкование. Сказано, что "void * - указатель на данные любого типа, и он приводится к указателю любого типа". С первой частью фразы всё нормально. А со второй - что такое "указатель любого типа"? Как построить фразу, если действительно нужно было бы утверждать, что некий указатель приводится к указателю не только на данные? Так и построить, как stringo это сделал, ведь - верно?
Простак(извини - как представился), в случае подобного выкрутаса на PL/1, была бы ошибка Protection Violation at ...
Ну, не умеет ни один нормальный компилятор такую патологию разруливать. Извини еще раз.
Простак(извини - как представился), в случае подобного выкрутаса на PL/1, была бы ошибка Protection Violation at ...
Ну, не умеет ни один нормальный компилятор такую патологию разруливать. Извини еще раз.
Виноват, С умеет. Деструктор срабатывает.
При чём тут PL/1?
Здесь никакая не патология, а небрежно составленное утверждение. Для C++ - ещё и неверное. Не приводится в C++ указатель на void к указателю на другой тип данных без всякого предупреждения. Более того, это трактуется как ошибка, а не как предупреждение.
stringo: В C/C++ есть ещё применения для типа void. Например, void * - указатель на данные любого типа. Такой указатель приводится к указателю любого типа безо всякого предупреждения
Неправда.
C:
Компилятор ругается вполне определённым образом:
C++:
int main() {
void *p(&p); // p - указатель на voidint *i(p); // i - указатель "любого типа", в данном случае - указатель на intint (*f)()(p); // f - указатель "не менее любого типа", в данном случае - указатель на функциюreturn0;
}
Компилятор также ругается вполне определённым образом в обоих случаях:
Кстати, так и не ответили на вопрос, как переменные могут считаться использованными, если для изобретённого вида "использования" (есть инициализация - значит, есть использование) при удалении из любой программы переменных, "использованных" именно таким образом, логическое поведение этой любой программы абсолютно не меняется.
Для данных примеров, кстати, для C:
с точки зрения "взрослого" компилятора, переменная f НЕ является использованной, несмотря на то, что она инициализирована.
Для C++:
с точки зрения "взрослого" компилятора, и переменная i, и переменная f, - обе НЕ являются использованными, несмотря на то, что обе инициализированы.
Логика "использованности", по крайней мере, для встроенных типов, очень проста: если значение переменной НЕ прочитано, она НЕ использована. Именно поэтому "взрослый" компилятор считает, что в обоих случаях переменная p ЯВЛЯЕТСЯ использованной.
У меня это тоже не компилируется:
error C2065: p: необъявленный идентификатор
Вот это компилируется и работает:
void main()
{
typedef int (*pf)();
void *p = nullptr; // p - указатель на voidint *i = (int*)p; // i - указатель "любого типа", в данном случае - указатель на intint (*f)() = (pf)p; // f - указатель "не менее любого типа", в данном случае - указатель на функцию
}
// или так:void main()
{
typedef int (*pf)();
void *p = nullptr; // p - указатель на voidint *i((int*)p); // i - указатель "любого типа", в данном случае - указатель на intint (*f)()((pf)p); // f - указатель "не менее любого типа", в данном случае - указатель на функцию
}
Так что, утверждение Славы верно. Указатель типа void* именно приводится к любому типу. Формально, конструктор не приводит к типу, а инициализирует. Если не предусмотрены шаблон или перегрузка конструктора для какого-либо типа, то это не значит, что нельзя привести тип. Это означает, что непредусмотренным типом инициализировать нельзя. Базовые типы указателей не обязаны уметь инициализировать себя через конструктор, чем попало.
Если это так, значит, использованный компилятор не соответствует стандарту C++.
В момент начала инициализации, то есть, когда когда за идентификатором p обнаружена открывающая круглая скобка, переменная уже существует, хоть ещё и не инициализирована, и - более того - для неё выделена память. Действительно, инициализируется значением некая область памяти, а не "воздух", поэтому, к моменту инициализации, память должна быть обязательно выделена. А раз память выделена, то имеется и вполне определённый её адрес, который и является адресом переменной. Соответственно, в выражении, используемом для инициализации, правомерно использовать адрес этой самой инициализируемой переменной. То же самое, кстати, относится и к C.
Касательно С++ и даже того же MQL4++. В конструкторе класса при инициализации объекта в инициализирующей части конструктора (между ":" и "{") точно так же и по той же причине правомерно использовать this, который является адресом - на момент использования this - ещё не инициализированного объекта класса. Например, для того же MQL4++:
#property strict/******************************************************************************/class A {
private:
/******************************************************************************/
A *a;
public:
/******************************************************************************/
A(): a(GetPointer(this)) {
}
/******************************************************************************/void fun() const {
Print("a = ", a);
}
};
/******************************************************************************/voidOnStart() {
A a;
a.fun();
}
В инициализирующей части конструктора был использован this ещё неинициализированного объекта. Тем не менее, данный пример прекрасно компилируется без всяких предупреждений и прекрасно работает:
void main()
{
typedef int (*pf)();
void *p = nullptr; // p - указатель на voidint *i = (int*)p; // i - указатель "любого типа", в данном случае - указатель на intint (*f)() = (pf)p; // f - указатель "не менее любого типа", в данном случае - указатель на функцию
}
// или так:void main()
{
typedef int (*pf)();
void *p = nullptr; // p - указатель на voidint *i((int*)p); // i - указатель "любого типа", в данном случае - указатель на intint (*f)()((pf)p); // f - указатель "не менее любого типа", в данном случае - указатель на функцию
}
Здесь применяется явное приведение, а существует, ведь, ещё и неявное. Кстати, прямая инициализация (вторая часть, помеченная комментарием "или так") для первого указателя выглядит следующим образом:
void *p(nullptr); // p - указатель на void
Zhunko:
Так что, утверждение Славы верно. Указатель типа void* именно приводится к любому типу.
Утверждение звучит следующим образом: "Например, void * - указатель на данные любого типа. Такой указатель приводится к указателю любого типа безо всякого предупреждения".
В утверждении не сказано, какого типа приведение имеется ввиду, явное или неявное. Однако, в утверждении имеется дополнение: "безо всякого предупреждения". Очевидно, что это позволяет вполне обоснованно предполагать, что имелось ввиду именно неявное приведение. В самом деле, возьмём тот же MQL4++:
#property strict/******************************************************************************/voidOnStart() {
long l = 5;
int i = l; /* line 6 */// ^ position 9int j = (int)l; /* line 7 */
}
Компилятор совершенно определённо предупреждает о возможных последствиях при неявном приведении и совсем НЕ предупреждает при явном:
possible loss of data due to type conversion 3.mq4 69
Точно так же в смысле предупреждений при неявном приведении ведут себя и компиляторы C/C++.
Более того, здесь, на форуме, можно встретить неоднократные советы применить явное приведение вместо неявного, чтобы избавиться от предупреждения компилятора о возможных последствиях.
Если нет явного приведения, это ещё не значит, что приведения нет вообще.
Zhunko:
Формально, конструктор не приводит к типу, а инициализирует. Если не предусмотрены шаблон или перегрузка конструктора для какого-либо типа, то это не значит, что нельзя привести тип. Это означает, что непредусмотренным типом инициализировать нельзя. Базовые типы указателей не обязаны уметь инициализировать себя через конструктор, чем попало.
У встроенных и производных типов (следует отличать от типов, определяемых пользователем, каковыми являются, например, классы), например, у такого производного типа, как указатель, нет никаких конструкторов. При отсутствии явного приведения, всё, что требуется в данном случае от выражения, использованного для инициализации, - чтобы тип этого выражения был неявно приводим к типу инициализируемой переменной, то есть, к типу указателя на соответствующий тип.
Zhunko:
Т.е. в Вашем примере отсутствует приведение типа.
А как же тогда быть с тем, что компилятор для моего примера выдаёт вполне однозначные сообщения об ошибках? Вот эти:
try.cpp:3:10: error: invalid conversion from'void*' to 'int*' [-fpermissive]
try.cpp:4:14: error: invalid conversion from'void*' to 'int (*)()' [-fpermissive]
Одно из двух: или компилятор нагло врёт, ведя речь о какой-то там "conversion" между парой типов 'void*' и 'int*' и парой 'void*' и 'int (*)()', или кто-то ошибся в своём утверждении об отсутствии приведения типа в моём примере.
Скорее всего, кроме небрежности с "любым типом указателя", stringo либо просто ошибся "в направлении приведения" для C++ (это к указателю на void указатель на другой тип данных приводится неявно и без предупреждений) либо забыл, что в C++ с приведениями - строже, чем в C, и неявное приведение там допускается только к указателю на void (в одну сторону, а не в обе).
1. Если это так, значит, использованный компилятор не соответствует стандарту C++.
2. В момент начала инициализации, то есть, когда когда за идентификатором p обнаружена открывающая круглая скобка, переменная уже существует, хоть ещё и не инициализирована, и - более того - для неё выделена память. Действительно, инициализируется значением некая область памяти, а не "воздух", поэтому, к моменту инициализации, память должна быть обязательно выделена. А раз память выделена, то имеется и вполне определённый её адрес, который и является адресом переменной. Соответственно, в выражении, используемом для инициализации, правомерно использовать адрес этой самой инициализируемой переменной. То же самое, кстати, относится и к C.
3. В утверждении не сказано, какого типа приведение имеется ввиду, явное или неявное. Однако, в утверждении имеется дополнение: "безо всякого предупреждения". Очевидно, что это позволяет вполне обоснованно предполагать, что имелось ввиду именно неявное приведение. В самом деле, возьмём тот же MQL4++:
Компилятор совершенно определённо предупреждает о возможных последствиях при неявном приведении и совсем НЕ предупреждает при явном:
Точно так же в смысле предупреждений при неявном приведении ведут себя и компиляторы C/C++.
Более того, здесь, на форуме, можно встретить неоднократные советы применить явное приведение вместо неявного, чтобы избавиться от предупреждения компилятора о возможных последствиях.
Если нет явного приведения, это ещё не значит, что приведения нет вообще.
4. У встроенных и производных типов (следует отличать от типов, определяемых пользователем, каковыми являются, например, классы), например, у такого производного типа, как указатель, нет никаких конструкторов. При отсутствии явного приведения, всё, что требуется в данном случае от выражения, использованного для инициализации, - чтобы тип этого выражения был неявно приводим к типу инициализируемой переменной, то есть, к типу указателя на соответствующий тип.
5. А как же тогда быть с тем, что компилятор для моего примера выдаёт вполне однозначные сообщения об ошибках? Вот эти:
Одно из двух: или компилятор нагло врёт, ведя речь о какой-то там "conversion" между парой типов 'void*' и 'int*' и парой 'void*' и 'int (*)()', или кто-то ошибся в своём утверждении об отсутствии приведения типа в моём примере.
6. Скорее всего, кроме небрежности с "любым типом указателя", stringo либо просто ошибся "в направлении приведения" для C++ (это к указателю на void указатель на другой тип данных приводится неявно и без предупреждений) либо забыл, что в C++ с приведениями - строже, чем в C, и неявное приведение там допускается только к указателю на void (в одну сторону, а не в обе).
1. Да, Майкрософт всегда снабжает студию компилятором несоответствующим стандарту С++ :-)) Это обычное дело :-))
2. Компилятор видит переменную, как объявленную, сразу после завершения оператора. Т.е. после ";". Так всегда было.
3. Использование неявного приведения это действие на авось. Вдруг прокатит. Вот у вас в примере не прокатило. Не нашлось подходящего конструктора.
4. Как это, нет конструктора? Если пример ниже компилируется, то конструктор есть.
void main()
{
typedef int (*pf)();
void *p = nullptr; // p - указатель на voidint *i((int*)p); // i - указатель "любого типа", в данном случае - указатель на intint (*f)()((pf)p); // f - указатель "не менее любого типа", в данном случае - указатель на функцию
}
Все базовые типы имеют конструкторы.
5. У Вас какой-то "левый" компилятор. Или, возможно, сильно устаревший. У меня так:
1>(416): error C2440: инициализация: невозможно преобразовать "void *" в "int *" 1> Для преобразования "void*" к указателю на тип, не являющемуся "void", требуется явное приведение 1>(417): error C2440: инициализация: невозможно преобразовать "void *" в "int (__cdecl *)(void)" 1> Для преобразования "void*" к указателю на тип, не являющемуся "void", требуется явное приведение
Компилятор обнаружил в единственном конструкторе невозможное присваивание. Всё разжевал и сразу предложил сделать явное приведение.
6. Слава всё правильно написал. Суть, повторюсь, в том, что у Вас нет приведения (явного приведения). Неявное можете сами сделать с помощью обёртки для базового типа. Дополните ещё одним конструктором.
2. Хоть какие-нибудь аргументы можно увидеть, кроме "так всегда было"? И заодно объяснить следующее явление, нагло имеющее место не только в C++, но даже в MQL4++:
#property strict/******************************************************************************/voidOnStart() {
int i = 7, j = i + 1;
Print("i = ", i, ", j = ", j);
}
Которое имеет наглость ещё и работать, как ожидается:
Как хорошо видно, в конструкции "int i = 7, j = i + 1;" для инициализации переменной j в выражении используется даже не адрес, а значение переменной i, хотя до "завершения оператора", то есть, до ";", дело ещё не дошло. Как это сопоставить с "так всегда было"?
Что ж, наверное следует обратиться к первоисточнику и, без преувеличения, - истине в последней инстанции, то есть, к стандарту C++11 (использован текущий драфт N3690):
3.3.2 Point of declaration [basic.scope.pdecl]
1 The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its
initializer (if any), except as noted below. [ Example:
int x = 12;
{ int x = x; }
Here the second x is initialized with its own (indeterminate) value. — end example ]
Совершенно ясно сказано, что точка декларации находится сразу после полного декларатора и до его инициализации, то есть, в моём примере, - на открывающей круглой скобке после имени переменной, как я и говорил. Более того, в только что процитированной мной части стандарта приведён пример, очень близкий к нашему случаю, (во внутреннем блоке, образованном { }) и далее прямо написано, что для этого случая переменная инициализируется своим собственным же (неопределённым) значением. Если уж прямо в стандарте пример приведён, - тут уж никуда не деться...
3. Это не у меня в примере "не прокатило", а утверждение stringo оказалось неверным, мой пример лишь был специально так составлен, чтобы это показать.
4. Хотелось бы ясно увидеть, как из того, что пример компилируется, следует, что конструктор есть. И ссылку на хоть какой-нибудь вменяемый документ, где утверждается, что в C++ все базовые типы имеют конструкторы.
5. gcc version 4.7.2. Не настолько и устаревший.
Zhunko:
У меня так:
1>(416): error C2440: инициализация: невозможно преобразовать "void *" в "int *" 1> Для преобразования "void*" к указателю на тип, не являющемуся "void", требуется явное приведение 1>(417): error C2440: инициализация: невозможно преобразовать "void *" в "int (__cdecl *)(void)" 1> Для преобразования "void*" к указателю на тип, не являющемуся "void", требуется явное приведение
Компилятор обнаружил в единственном конструкторе невозможное присваивание. Всё разжевал и сразу предложил сделать явное приведение.
6. Слава всё правильно написал. Суть, повторюсь, в том, что у Вас нет
приведения (явного приведения). Неявное можете сами сделать с помощью
обёртки для базового типа. Дополните ещё одним конструктором.
Нет там никакого присваивания, там - инициализация, это сильно разные вещи. Более того, в сообщении об ошибках компилятор об этом прямым текстом и заявляет, цитирую: "error C2440: инициализация:". Инициализация, а не "невозможное присваивание". В сообщениях об ошибках также сказано о том, что, цитирую: "невозможно преобразовать" и "требуется явное приведение". Ничего об обнаружении "невозможного присваивания" там не сказано. И про конструкторы ни слова. Зачем же выдумывать?
6. Я специально привёл пример, чтобы показать, что stringo ошибся. Да, суть в том, что в C++ указатель на void НЕ приводится НЕЯВНО к указателю на тип данных, отличных от типа void. Вот, в обратную сторону - приводится НЕЯВНО, а так, как утверждал stringo - нет.
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 - нет.
Не выдёргивается толкование. Сказано, что "void * - указатель на данные любого типа, и он приводится к указателю любого типа". С первой частью фразы всё нормально. А со второй - что такое "указатель любого типа"? Как построить фразу, если действительно нужно было бы утверждать, что некий указатель приводится к указателю не только на данные? Так и построить, как stringo это сделал, ведь - верно?
Простак(извини - как представился), в случае подобного выкрутаса на PL/1, была бы ошибка Protection Violation at ...
Ну, не умеет ни один нормальный компилятор такую патологию разруливать. Извини еще раз.
Виноват, С умеет. Деструктор срабатывает.
Простак(извини - как представился), в случае подобного выкрутаса на PL/1, была бы ошибка Protection Violation at ...
Ну, не умеет ни один нормальный компилятор такую патологию разруливать. Извини еще раз.
Виноват, С умеет. Деструктор срабатывает.
При чём тут PL/1?
Здесь никакая не патология, а небрежно составленное утверждение. Для C++ - ещё и неверное. Не приводится в C++ указатель на void к указателю на другой тип данных без всякого предупреждения. Более того, это трактуется как ошибка, а не как предупреждение.
В C нет деструкторов.
В C нет деструкторов.
simpleton:
В C/C++ есть ещё применения для типа void. Например, void * - указатель на данные любого типа. Такой указатель приводится к указателю любого типа безо всякого предупреждения
C:
Компилятор ругается вполне определённым образом:
C++:
Компилятор также ругается вполне определённым образом в обоих случаях:Кстати, так и не ответили на вопрос, как переменные могут считаться использованными, если для изобретённого вида "использования" (есть инициализация - значит, есть использование) при удалении из любой программы переменных, "использованных" именно таким образом, логическое поведение этой любой программы абсолютно не меняется.
Для данных примеров, кстати, для C:
с точки зрения "взрослого" компилятора, переменная f НЕ является использованной, несмотря на то, что она инициализирована.
Для C++:
с точки зрения "взрослого" компилятора, и переменная i, и переменная f, - обе НЕ являются использованными, несмотря на то, что обе инициализированы.
Логика "использованности", по крайней мере, для встроенных типов, очень проста: если значение переменной НЕ прочитано, она НЕ использована.
Именно поэтому "взрослый" компилятор считает, что в обоих случаях переменная p ЯВЛЯЕТСЯ использованной.
У меня это тоже не компилируется:
Вот это компилируется и работает:
Так что, утверждение Славы верно. Указатель типа void* именно приводится к любому типу. Формально, конструктор не приводит к типу, а инициализирует. Если не предусмотрены шаблон или перегрузка конструктора для какого-либо типа, то это не значит, что нельзя привести тип. Это означает, что непредусмотренным типом инициализировать нельзя. Базовые типы указателей не обязаны уметь инициализировать себя через конструктор, чем попало.
Т.е. в Вашем примере отсутствует приведение типа.
У меня это тоже не компилируется:
Видимо, не компилируется строка:
void *p(&p);Если это так, значит, использованный компилятор не соответствует стандарту C++.
В момент начала инициализации, то есть, когда когда за идентификатором p обнаружена открывающая круглая скобка, переменная уже существует, хоть ещё и не инициализирована, и - более того - для неё выделена память. Действительно, инициализируется значением некая область памяти, а не "воздух", поэтому, к моменту инициализации, память должна быть обязательно выделена. А раз память выделена, то имеется и вполне определённый её адрес, который и является адресом переменной. Соответственно, в выражении, используемом для инициализации, правомерно использовать адрес этой самой инициализируемой переменной. То же самое, кстати, относится и к C.
Касательно С++ и даже того же MQL4++. В конструкторе класса при инициализации объекта в инициализирующей части конструктора (между ":" и "{") точно так же и по той же причине правомерно использовать this, который является адресом - на момент использования this - ещё не инициализированного объекта класса. Например, для того же MQL4++:
В инициализирующей части конструктора был использован this ещё неинициализированного объекта. Тем не менее, данный пример прекрасно компилируется без всяких предупреждений и прекрасно работает:
Вот это компилируется и работает:
Так что, утверждение Славы верно. Указатель типа void* именно приводится к любому типу.
Утверждение звучит следующим образом: "Например, void * - указатель на данные любого типа. Такой указатель приводится к указателю любого типа безо всякого предупреждения".
В утверждении не сказано, какого типа приведение имеется ввиду, явное или неявное. Однако, в утверждении имеется дополнение: "безо всякого предупреждения". Очевидно, что это позволяет вполне обоснованно предполагать, что имелось ввиду именно неявное приведение. В самом деле, возьмём тот же MQL4++:
Компилятор совершенно определённо предупреждает о возможных последствиях при неявном приведении и совсем НЕ предупреждает при явном:
Точно так же в смысле предупреждений при неявном приведении ведут себя и компиляторы C/C++.
Более того, здесь, на форуме, можно встретить неоднократные советы применить явное приведение вместо неявного, чтобы избавиться от предупреждения компилятора о возможных последствиях.
Если нет явного приведения, это ещё не значит, что приведения нет вообще.
Zhunko:
Формально, конструктор не приводит к типу, а инициализирует. Если не предусмотрены шаблон или перегрузка конструктора для какого-либо типа, то это не значит, что нельзя привести тип. Это означает, что непредусмотренным типом инициализировать нельзя. Базовые типы указателей не обязаны уметь инициализировать себя через конструктор, чем попало.
Zhunko:
Т.е. в Вашем примере отсутствует приведение типа.
А как же тогда быть с тем, что компилятор для моего примера выдаёт вполне однозначные сообщения об ошибках? Вот эти:
Одно из двух: или компилятор нагло врёт, ведя речь о какой-то там "conversion" между парой типов 'void*' и 'int*' и парой 'void*' и 'int (*)()', или кто-то ошибся в своём утверждении об отсутствии приведения типа в моём примере.
Скорее всего, кроме небрежности с "любым типом указателя", stringo либо просто ошибся "в направлении приведения" для C++ (это к указателю на void указатель на другой тип данных приводится неявно и без предупреждений) либо забыл, что в C++ с приведениями - строже, чем в C, и неявное приведение там допускается только к указателю на void (в одну сторону, а не в обе).
Видимо, не компилируется строка:
1. Если это так, значит, использованный компилятор не соответствует стандарту C++.
2. В момент начала инициализации, то есть, когда когда за идентификатором p обнаружена открывающая круглая скобка, переменная уже существует, хоть ещё и не инициализирована, и - более того - для неё выделена память. Действительно, инициализируется значением некая область памяти, а не "воздух", поэтому, к моменту инициализации, память должна быть обязательно выделена. А раз память выделена, то имеется и вполне определённый её адрес, который и является адресом переменной. Соответственно, в выражении, используемом для инициализации, правомерно использовать адрес этой самой инициализируемой переменной. То же самое, кстати, относится и к C.
3. В утверждении не сказано, какого типа приведение имеется ввиду, явное или неявное. Однако, в утверждении имеется дополнение: "безо всякого предупреждения". Очевидно, что это позволяет вполне обоснованно предполагать, что имелось ввиду именно неявное приведение. В самом деле, возьмём тот же MQL4++:
Компилятор совершенно определённо предупреждает о возможных последствиях при неявном приведении и совсем НЕ предупреждает при явном:
Точно так же в смысле предупреждений при неявном приведении ведут себя и компиляторы C/C++.
Более того, здесь, на форуме, можно встретить неоднократные советы применить явное приведение вместо неявного, чтобы избавиться от предупреждения компилятора о возможных последствиях.
Если нет явного приведения, это ещё не значит, что приведения нет вообще.
4. У встроенных и производных типов (следует отличать от типов, определяемых пользователем, каковыми являются, например, классы), например, у такого производного типа, как указатель, нет никаких конструкторов. При отсутствии явного приведения, всё, что требуется в данном случае от выражения, использованного для инициализации, - чтобы тип этого выражения был неявно приводим к типу инициализируемой переменной, то есть, к типу указателя на соответствующий тип.
5. А как же тогда быть с тем, что компилятор для моего примера выдаёт вполне однозначные сообщения об ошибках? Вот эти:
Одно из двух: или компилятор нагло врёт, ведя речь о какой-то там "conversion" между парой типов 'void*' и 'int*' и парой 'void*' и 'int (*)()', или кто-то ошибся в своём утверждении об отсутствии приведения типа в моём примере.
6. Скорее всего, кроме небрежности с "любым типом указателя", stringo либо просто ошибся "в направлении приведения" для C++ (это к указателю на void указатель на другой тип данных приводится неявно и без предупреждений) либо забыл, что в C++ с приведениями - строже, чем в C, и неявное приведение там допускается только к указателю на void (в одну сторону, а не в обе).
1. Да, Майкрософт всегда снабжает студию компилятором несоответствующим стандарту С++ :-)) Это обычное дело :-))
2. Компилятор видит переменную, как объявленную, сразу после завершения оператора. Т.е. после ";". Так всегда было.
3. Использование неявного приведения это действие на авось. Вдруг прокатит. Вот у вас в примере не прокатило. Не нашлось подходящего конструктора.
4. Как это, нет конструктора? Если пример ниже компилируется, то конструктор есть.
Все базовые типы имеют конструкторы.
5. У Вас какой-то "левый" компилятор. Или, возможно, сильно устаревший. У меня так:
1>(416): error C2440: инициализация: невозможно преобразовать "void *" в "int *"
1> Для преобразования "void*" к указателю на тип, не являющемуся "void", требуется явное приведение
1>(417): error C2440: инициализация: невозможно преобразовать "void *" в "int (__cdecl *)(void)"
1> Для преобразования "void*" к указателю на тип, не являющемуся "void", требуется явное приведение
Компилятор обнаружил в единственном конструкторе невозможное присваивание. Всё разжевал и сразу предложил сделать явное приведение.
6. Слава всё правильно написал. Суть, повторюсь, в том, что у Вас нет приведения (явного приведения). Неявное можете сами сделать с помощью обёртки для базового типа. Дополните ещё одним конструктором.
1. Да, Майкрософт всегда снабжает студию компилятором несоответствующим стандарту С++ :-)) Это обычное дело :-))
2. Компилятор видит переменную, как объявленную, сразу после завершения оператора. Т.е. после ";". Так всегда было.
3. Использование неявного приведения это действие на авось. Вдруг прокатит. Вот у вас в примере не прокатило. Не нашлось подходящего конструктора.
4. Как это, нет конструктора? Если пример ниже компилируется, то конструктор есть.
Все базовые типы имеют конструкторы.
5. У Вас какой-то "левый" компилятор. Или, возможно, сильно устаревший.
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. Не настолько и устаревший.
Zhunko:
У меня так:
1>(416): error C2440: инициализация: невозможно преобразовать "void *" в "int *"
1> Для преобразования "void*" к указателю на тип, не являющемуся "void", требуется явное приведение
1>(417): error C2440: инициализация: невозможно преобразовать "void *" в "int (__cdecl *)(void)"
1> Для преобразования "void*" к указателю на тип, не являющемуся "void", требуется явное приведение
Компилятор обнаружил в единственном конструкторе невозможное присваивание. Всё разжевал и сразу предложил сделать явное приведение.
6. Слава всё правильно написал. Суть, повторюсь, в том, что у Вас нет приведения (явного приведения). Неявное можете сами сделать с помощью обёртки для базового типа. Дополните ещё одним конструктором.
Нет там никакого присваивания, там - инициализация, это сильно разные вещи. Более того, в сообщении об ошибках компилятор об этом прямым текстом и заявляет, цитирую: "error C2440: инициализация:". Инициализация, а не "невозможное присваивание". В сообщениях об ошибках также сказано о том, что, цитирую: "невозможно преобразовать" и "требуется явное приведение". Ничего об обнаружении "невозможного присваивания" там не сказано. И про конструкторы ни слова. Зачем же выдумывать?
6. Я специально привёл пример, чтобы показать, что stringo ошибся. Да, суть в том, что в C++ указатель на void НЕ приводится НЕЯВНО к указателю на тип данных, отличных от типа void. Вот, в обратную сторону - приводится НЕЯВНО, а так, как утверждал stringo - нет.
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 - нет.
Вы сами-то поняли, что сказали?
Вы сами-то поняли, что сказали?