Выбрасывает std:: pair <T1, T2> const & to std:: pair <T1 const, T2> const & safe?
Безопасно (теоретически или на практике) reinterpret_cast
a std::pair<T1, T2> const &
в std::pair<T1 const, T2> const &
, предполагая, что программист не намеренно сделал что-то странное, как специализирующееся std::pair<T1 const, T2>
?
Ответы
Ответ 1
Это не переносимо для этого.
std::pair
требования изложены в пункте 20.3. В пункте 17.5.2.3 разъясняется, что
В пунктах с 18 по 30 и в Приложении D не указывается представление классов и умышленно не указывается спецификация членов класса. Реализация может определять статические или нестатические члены класса или и то, и другое по мере необходимости для реализации семантики функций-членов, указанных в пунктах с 18 по 30 и в приложении D.
Это подразумевает, что он юридически (хотя и невероятно маловероятен) для реализации включает в себя частичную специализацию, такую как:
template<typename T1, typename T2>
struct pair<T1, T2>
{
T1 first;
T2 second;
};
template<typename T1, typename T2>
struct pair<const T1, T2>
{
T2 second;
const T1 first;
};
которые явно не совместимы с макетами. Другие правила, включая включение дополнительных нестатических членов данных, возможно, до first
и/или second
также разрешены в соответствии с правилом.
Теперь несколько интереснее рассмотреть случай, когда макет известен. Хотя Potatoswatter указал на DR1334, который утверждает, что T
и const T
не совместимы с макетами, Стандарт предоставляет достаточные гарантии, чтобы позволить нам получить в любом случае:
template<typename T1, typename T2>
struct mypair<T1, T2>
{
T1 first;
T2 second;
};
mypair<int, double> pair1;
mypair<int, double>* p1 = &pair1;
int* p2 = reinterpret_cast<int*>(p1); // legal by 9.2p20
const int* p3 = p2;
mypair<const int, double>* p4 = reinterpret_cast<mypair<const int, double>*>(p3); // again 9.2p20
Однако это не работает на std::pair
, поскольку мы не можем применить 9.2p20, не зная, что first
на самом деле является исходным элементом, который не указан.
Ответ 2
pair
определяется в разделе 20.3.2 стандарта, чтобы иметь данные:
template <class T1, class T2>
struct pair {
T1 first;
T2 second;
};
Это означает, что для конкретных типов T1
, T2
, pair<T1, T2>
и pair<const T1, T2>
гарантированы соответствующие члены данных:
struct pair<T1, T2> {
T1 first;
T2 second;
};
struct pair<const T1, T2> {
const T1 first;
T2 second;
};
Теперь, если T1
и T2
являются стандартными макетами, то pair<T1, T2>
и pair<const T1, T2>
являются стандартными макетами. Как обсуждалось выше, DR1334 они не совместимы с макетами (3.9p11), но на 9.2p19 они могут быть reinterpret_cast
к их соответствующим T1
или const T1
первого члена. На 9.2p13 второй член T2
должен быть расположен после того, как первый член (то есть с более высоким адресом) и на 1.8p5 должен быть расположен сразу после первого элемента таким образом, чтобы объект был смежным после учета выравнивания (9.2p19).
Мы можем проверить это с помощью offsetof
(который определен для типов стандартного макета):
static_assert(offsetof(pair<T1, T2>, second) ==
offsetof(pair<const T1, T2>, second), "!");
Так как pair<T1, T2>
и pair<const T1, T2>
имеют один и тот же макет, кастинг в прямом направлении и использование результата для доступа к членам действительны по 3.9.2p3:
Если объект типа T
расположен по адресу A
, указатель типа cv T*
, значение которого является адресом A
, как говорят, указывает на этот объект, независимо от того, как это значение было Полученный.
Итак, reinterpret_cast
безопасен, только если std::is_standard_layout<std::pair<T1, T2>>::value
- true
.
Ответ 3
Практический ответ заключается в том, что приведение в const должно быть безопасным, так как вы переводите текст на объект с идентичным представлением. Однако, наоборот, вводится поведение undefined (const неконстантно).
Что касается "теоретического" ответа, я должен отметить, что стандарт С++ не гарантирует идентичного поразрядного представления объектов const/non-const. Ключевое слово const гарантирует "концептуальную константу", которая зависит от реализации.