Преобразование из int** в const int**
почему я получаю в этом коде:
void foo ( const int ** );
int main() {
int ** v = new int * [10];
foo(v);
return 0;
}
эта ошибка:
invalid conversion from ‘int**’ to ‘const int**’ [-fpermissive]|
Я думал, что можно будет преобразовать из non-const в const.
3 ответов
это потому, что вы пытаетесь конвертировать из int** to const int**
int ** v = new int * [10]; // v is int**
foo(v); //but foo takes const int**
-
int **
is: "указатель на указатель на целое число". -
const int **
is: "указатель на указатель на постоянное целое число".
использование const
является контрактом, и вы не можете выполнить этот контракт, пройдя через косвенное две ссылки.
из стандартного:
const char c = 'c';
char* pc;
const char** pcc = &pc; // not allowed (thankfully!)
^^^ here the bundit is hidden under const: "I will not modify"
*pcc = &c; // *pcc is "pointer to const" right? so this is allowed...
*pc = 'C'; // would allow to modify a const object, *pc is char right?
таким образом, можно было бы изменить const char
всегда, просто использовать процедура выше.
а также:
char *s1 = 0;
const char *s2 = s1; // OK...
char *a[MAX]; // aka char **
const char * const*ps = a; // no error!
приятно цитировать по ссылке ниже:
по аналогии, если вы скрываете преступника под законной личиной, он затем можно использовать доверие, данное этой маскировке. Это плохо.
http://www.parashift.com/c++-faq-lite/constptrptr-conversion.html
связанный с этим также недействителен преобразование Derived** → Base**
. Если бы это было законно, чтобы преобразовать Derived** → Base**
, the Base**
может быть разыменован (дает Base*
), а база* может указывать на объект другого производного класса, что может вызвать серьезные проблемы. Смотрите почему:
class Vehicle {
public:
virtual ~Vehicle() { }
virtual void startEngine() = 0;
};
class Car : public Vehicle {
public:
virtual void startEngine();
virtual void openGasCap();
};
class NuclearSubmarine : public Vehicle {
public:
virtual void startEngine();
virtual void fireNuclearMissle();
};
int main()
{
Car car;
Car* carPtr = &car;
Car** carPtrPtr = &carPtr;
Vehicle** vehiclePtrPtr = carPtrPtr; // This is an error in C++
NuclearSubmarine sub;
NuclearSubmarine* subPtr = ⊂
*vehiclePtrPtr = subPtr;
// This last line would have caused carPtr to point to sub !
carPtr->openGasCap(); // This might call fireNuclearMissle()!
...
}
http://www.parashift.com/c++-faq-lite/derivedptrptr-to-baseptrptr.html
считаем:
class Vehicle {
public:
virtual ~Vehicle() { }
virtual void startEngine() = 0;
};
class Car : public Vehicle {
public:
virtual void startEngine(){printf("Car engine brummm\n");}
virtual void openGasCap(){printf("Car: open gas cap\n");}
virtual void openGasCap2(){printf("Car: open gas cap2\n");}
virtual void openGasCap3(){printf("Car: open gas cap3\n");}
virtual void openGasCap4(){printf("Car: open gas cap4\n");}
};
class NuclearSubmarine : public Vehicle {
public:
int i;
virtual void startEngine(){printf("Nuclear submarine engine brummm\n");}
virtual void fireNuclearMissle3(){printf("Nuclear submarine: fire the missle3!\n");}
virtual void fireNuclearMissle(){printf("Nuclear submarine: fire the missle!\n");}
virtual void fireNuclearMissle2(){printf("Nuclear submarine: fire the missle2!\n");}
};
int main(){
Car car; Car* carPtr = &car;
Car** carPtrPtr = &carPtr;
//Vehicle** vehiclePtrPtr = carPtrPtr; // This is an error in C++, But:
Vehicle** vehiclePtrPtr = reinterpret_cast<Vehicle**>(carPtrPtr);
NuclearSubmarine sub; NuclearSubmarine* subPtr = ⊂
*vehiclePtrPtr = subPtr; // carPtr points to sub !
carPtr->openGasCap(); // Nuclear submarine: fire the missle3!
carPtr->openGasCap2(); // Nuclear submarine: fire the missle!
carPtr->openGasCap3(); // Nuclear submarine: fire the missle2!
//carPtr->openGasCap4(); // SEG FAULT
}
вы можете добавить квалификацию const только в преобразовании между аналогичными типами указателей, если вы добавляете const на всех уровнях от первой разницы в квалификации cv и выше.
таким образом, вы можете преобразовать int**
to int const* const*
, а не int const* *
. Если бы было разрешено опустить добавление const на промежуточных уровнях, вы могли бы сделать что-то вроде:
const int c = 29;
int *pi;
const int** ppci = π // only adding const, right
*ppci = &c;
*pi = 0; // changing c ?! but no const_cast in sight
вас вводят в заблуждение запутанные правила синтаксического анализа C++для указателей. Возможно, было бы яснее взглянуть на это так:
typedef const int * ptr_to_const_int;
void foo( ptr_to_const_int *);
int main() {
int ** v = new int * [10];
foo(v);
return 0;
}
список параметров foo () обещает, что вы передадите ему указатель на (указатель на постоянную вещь). Но новый int * [10] означает "указатель на (указатель на не-постоянную вещь)".
Итак, если Foo были определены следующим образом:
foo( const int **p )
{
(*p); //<-- this is actually of type const int *
}
а я думаю, что вы ожидали, что
foo( const int **p )
{
(*p); //<-- you're expecting this to be of type int *
p = 0; //<-- and this to throw a compiler error because p is const
}
но это не p, что вы заявляете, что вы постоянны, это то, на что он указывает.
В любом случае просто используйте typedef в этом случае, и все будет ясно и читабельно.