вопрос довольно прост, является ли вообще безопасным статическое приведение (или какое-либо другое приведение) от
std::vector< Foo >
в
std::vector< const Foo >
бинарно, я не понимаю, почему нативные типы будут отличаться, в конце концов, const
это ограничение языка, которое не должно влиять на размер элемента, или я так думаю
я могу сделать
std::vector< const Foo >& someFunc()
{
std::vector< Foo >& ref = ...
return *reinterpret_cast<std::vector< const Foo >*>(& ref);
}
и не волнуетесь, что это потопит чью-то лодку? или это вообще небезопасно?
Игнорируя то, что вы сказали на данный момент std :: vector, и притворившись, что у вас есть другая, менее четко определенная реализация вектора. Ваш код был бы технически небезопасен не потому, что T
а также T const
совсем другие, но потому что язык C ++ позволяет vector<T>
а также vector<T const>
быть специализированным в совершенно разных направлениях. Рассмотрим следующий код:
#include <iostream>
template <class T>
struct vector {
T* start_;
T* end_;
T* cap_end_;
};
template <class T>
struct vector<T const> {
bool gotcha_;
T* start_;
T* end_;
T* cap_end_;
};
struct foo { };
int
main()
{
std::cout
<< sizeof(vector<foo>) << '\n'
<< sizeof(vector<foo const>) << '\n'
;
}
Обратите внимание, что есть и другие более пагубные изменения, которые могут сделать вашу жизнь несчастной. Например, следующее, где члены переупорядочены:
#include <iostream>
template <class T>
struct vector {
T* start_;
T* end_;
T* cap_end_;
};
template <class T>
struct vector<T const> {
T* end_;
T* cap_end_;
T* start_;
};
template <class T>
long size(vector<T> const& v)
{
return v.end_ - v.start_;
}
struct foo { };
int
main()
{
vector<foo> v;
v.start_ = new foo[10];
v.end_ = v.start_ + 1;
v.cap_end_ = v.start_ + 10;std::cout
<< size(v) << '\n'
<< size(*reinterpret_cast<vector<foo const>*>(&v)) << '\n'
;
return 0;
}
Что касается std :: vector, я недостаточно знаком с тонкими деталями спецификации стандартной библиотеки, чтобы знать, будут ли такие специализации соответствовать или нет. Возможно, кто-то более хорошо разбирающийся в стандарте может прокомментировать.
Обратите внимание на то, что я сказал в ответ на Приведение шаблонного класса к более общей специализации может помочь объяснить эту проблему.
Чтобы ответить на ваш вопрос об обнаружении специализаций, есть способы сделать ваш код небезопасным, не используя специализаций класса, а перегружая функции, не являющиеся членами, и я не уверен, как вы это обнаружите. Например, в следующем:
#include <iostream>
template <class T>
struct vector {
T* start_;
T* end_;
T* cap_end_;
};template <class T>
void init(vector<T>& v, size_t sz, size_t cap)
{
v.start_ = new T[cap];
v.end_ = v.start_ + sz;
v.cap_end_ = v.start_ + cap;
}
template <class T>
void init(vector<T const>& v, size_t sz, size_t cap)
{
v.end_ = new T const[cap];
v.cap_end_ = v.end_ + sz;
v.start_ = v.end_ + cap;
}
template <class T>
long size(vector<T>& v)
{
return v.end_ - v.start_;
}
template <class T>
long size(vector<T const>& v)
{
return v.cap_end_ - v.end_;
}
struct foo { };
int
main()
{
vector<foo const> v;
init(v, 1, 10);
std::cout
<< size(v) << '\n'
<< size(*reinterpret_cast<vector<foo>*>(&v)) << '\n'
;
}
Хватит с плохих новостей. Хорошей новостью является то, что если вы хотите взять существующий объект с общим интерфейсом и ограничить или настроить то, что можно сделать с этим объектом, есть несколько простых, безопасных и приемлемых способов сделать это. Посмотрите на std :: stack http://www.sgi.com/tech/stl/stack.html или альтернативно этот ответ https://stackoverflow.com/a/994925/453436 в Что такое прокси-класс в C ++
Других решений пока нет …