Законно ли проверять, находится ли адрес подобъекта в границах содержащего объекта
2 вопроса:
-
Хорошо ли сформирован следующий код с определенным поведением?
-
Существует ли какая-либо возможная реализация c++, в которой он мог бы утверждать?
Код (c++11 и выше):
#include <cassert>
#include <utility>
#include <ciso646>
template<class T>
auto to_address(T* p) { return reinterpret_cast<unsigned char const*>(p); }
/// Test whether part is a sub-object of object
template<class Object, class Part>
bool is_within_object(Object& object, Part& part)
{
auto first = to_address(std::addressof(object)),
last = first + sizeof(Object);
auto p = to_address(std::addressof(part));
return (first <= p) and (p < last);
}
struct X
{
int a = 0;
int& get_a() { return a; }
int& get_b() { return b; }
private:
int b = 0;
};
int main()
{
X x;
assert(is_within_object(x, x.get_a()));
assert(is_within_object(x, x.get_b()));
}
Обратите внимание, что a
и b
имеют разные спецификаторы доступа.1 ответ:
Сравнение указателей определяется в [expr.рел]/3-4:
Какие выводы мы можем сделать из этого?Сравнение неравных указателей на объекты определяется следующим образом:
- если два указателя указывают на разные элементы одного и того же массива или на его подобъекты, то указатель на элемент с более высоким индексом сравнивается с большим.
- если два указателя указывают на разные нестатические элементы данных одного и того же объекта или на подобъекты таких элементов рекурсивно, то указатель на позже объявленный член сравнивается с большим, если два члена имеют одинаковый контроль доступа и если их класс не является объединением.
- в противном случае ни один указатель не сравнится больше, чем другой.
Если два операнда p и q сравниваются равными, p=q оба дают true и pq оба дают false. В противном случае, если указатель p сравнивает больше, чем указатель q, p>=q, p>q, qp все дают false. В противном случае результатом работы каждого из операторов будет неопределенный.
Существуетполный порядок указателей одного и того же типа внутри объекта, но нетпорядка указателей на различные объекты или различные подобъекты с различным управлением доступом. Это отсутствие общего общего порядка указателей делает
is_within_object()
не очень значимым. В тех случаях, когда вы ожидаете, что он вернетсяtrue
, он работает. В тех случаях, когда вы ожидаете, что он вернетсяfalse
, результат из этих операторов является неопределенным? Это не очень полезный результат.
Что сказал, У нас есть гигантская лазейка для этого в виде [сравнений]:
Для шаблонов
less
,greater
,less_equal
, иgreater_equal
, специализации для любого типа указателя дают строгий полный порядок, который согласуется между этими специализациями, а также согласуется с частичным порядком, налагаемым встроенными операторами<
,>
,<=
,>=
.Таким образом, будет четко определено следующее:
template<class T> auto byte_address(T& p) { return reinterpret_cast<std::byte const*>(std::addressof(p)); } template<class Object, class Part> bool is_within_object(Object& object, Part& part) { auto first = byte_address(object); auto last = first + sizeof(Object); auto p = byte_address(part); return std::less_equal<std::byte*>{}(first, p) && std::less<std::byte*>{}(p, last); }