Законно ли проверять, находится ли адрес подобъекта в границах содержащего объекта


2 вопроса:

  1. Хорошо ли сформирован следующий код с определенным поведением?

  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 12

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);
}