Может быть, стоит показать, почему нарушение константности позволяет выполнить преобразование, которое вы хотите:
#include <vector>
const int a = 1;
void addConst(std::vector<const int *> &v) {
v.push_back(&a); // this is OK, adding a const int* to a vector of same
}
int main() {
std::vector<int *> w;
int b = 2;
w.push_back(&b); // this is OK, adding an int* to a vector of same
*(w.back()) = 3; // this is OK, assigning through an int*
addConst(w); // you want this to be OK, but it isn't...
*(w.back()) = 3; // ...because it would make this const-unsafe.
}
Проблема в том, что vector<int*>.push_back
принимает указатель на неконстантный (который теперь я буду называть "неконстантным указателем"). Это означает, что он может изменить указатель своего параметра. В частности, в случае с вектором, он может передать указатель кому-то еще, кто его модифицирует. Таким образом, вы не можете передать константный указатель на функцию push_back функции w, и желаемое преобразование небезопасно, даже если система шаблонов его поддерживает (а это не так). Цель const-safety - предотвратить передачу константного указателя на функцию, которая принимает неконстантный указатель, и именно так она выполняет свою работу. C ++ требует от вас, чтобы вы конкретно сказали, хотите ли вы сделать что-то небезопасное, поэтому преобразование не может быть неявным. Фактически, из-за того, как работают шаблоны, это вообще невозможно (см. Позже).
Я думаю, что C ++ в принципе может сохранить const-безопасность, разрешив преобразование из vector<T*>&
в const vector<const T*>&
, так же как int **
в const int *const *
безопасно. Но это из-за способа определения вектора: он не обязательно будет безопасным для других шаблонов.
Точно так же теоретически можно разрешить явное преобразование. И фактически, он допускает явное преобразование, но только для объектов, а не ссылок; -)
std::vector<const int*> x(w.begin(), w.end()); // conversion
Причина, по которой он не может сделать это для ссылок, заключается в том, что система шаблонов не может его поддерживать. Другой пример, который был бы разбит, если бы преобразование было разрешено:
template<typename T>
struct Foo {
void Bar(T &);
};
template<>
struct Foo<const int *> {
void Baz(int *);
};
Теперь Foo<int*>
не имеет функции Baz. Как же указатель или ссылка на Foo<int*>
могут быть преобразованы в указатель или ссылку на Foo<const int*>
?
Foo<int *> f;
Foo<const int *> &g = f; // Not allowed, but suppose it was
int a;
g.Baz(&a); // Um. What happens? Calls Baz on the object f?