Смещение указателя на элемент

template<class T, typename U> ptrdiff_t foo(T U::* m)
{
    // return offset
}

Как я могу получить смещение поля «m» в этом контексте? Я бы предпочел использовать выражение времени компиляции am.

Заранее благодарю за любую помощь. С наилучшими пожеланиями


person 0xbadf00d    schedule 11.04.2011    source источник
comment
Это, наверное, плохая идея. Почему бы вместо этого не использовать тип указателя на член?   -  person GManNickG    schedule 11.04.2011
comment
Хм, что именно ты имеешь в виду? Параметр выше ЯВЛЯЕТСЯ указателем на член...   -  person 0xbadf00d    schedule 12.04.2011
comment
Правильно, и оставьте это на этом. Смещения просто слишком примитивны, чтобы их можно было использовать для большинства классов C++ (не POD).   -  person GManNickG    schedule 12.04.2011
comment
Хорошо, но вы написали «почему бы не использовать тип указателя на член ВМЕСТО?» Как вы это понимаете? Кстати: я мог бы написать специфичную для компилятора версию моей функции member_offset.   -  person 0xbadf00d    schedule 12.04.2011
comment
Я не могу предложить вместо этого использовать указатель на член, потому что у вас есть только запросил шаг, а не цель. Что вы пытаетесь достичь?   -  person GManNickG    schedule 12.04.2011
comment
@GManNickG Vulkan, OpenGL и любые другие графические библиотеки, использующие структурированные буферы, запрашивают смещения вместо указателей на член. В этом случае они не понимают указатели на члены, и вы должны преобразовать это в смещение.   -  person JoaoBapt    schedule 16.03.2021


Ответы (4)


Похоже, вы ищете макрос offsetof().

person Michael J    schedule 11.04.2011
comment
Недопустимо вызывать offsetof с указателем члена, только с именем члена. (во многих реализациях это может работать, но, учитывая, насколько неясны такие вещи, это может сломаться при любых обновлениях компилятора) - person Yakk - Adam Nevraumont; 09.03.2014
comment
Привет @Yakk. Я не совсем уверен, что вы имеете в виду. Если вы нажмете на слово offsetof (выше), вы перейдете к документам для макроса. Мне он кажется кошерным. Под указателем на член вы подразумеваете член структуры, который является указателем? например структура с { int я; символ *р; }; size_t n = offsetof(struct s, p); - person Michael J; 09.03.2014

@Майкл Дж

Спасибо за Ваш ответ. Это было не совсем то, что я искал, но это вдохновляет меня на это:

template<class T, typename U>
std::ptrdiff_t member_offset(U T::* member)
{
    return reinterpret_cast<std::ptrdiff_t>(
        &(reinterpret_cast<T const volatile*>(NULL)->*member)
    );
}
person 0xbadf00d    schedule 11.04.2011
comment
Здесь вы разыменовываете нулевой указатель. Это не разрешено, если только вы не реализуете библиотеку и не получаете специальное разрешение на использование в макросе offsetof. Кто-то опередил меня до -1. - person Bo Persson; 11.04.2011
comment
Вот что делает макрос offsetof: (size_t)&reinterpret_cast‹const volatile char&›((((s *)0)-›m)). Какая разница? - person 0xbadf00d; 11.04.2011
comment
Разница в том, что offsetof делает это для конкретного компилятора и только потому, что разработчик библиотеки заключил особое соглашение с автором компилятора. Тогда это может сработать. В других компиляторах offsetof вместо этого используется как специальная функция __builtin_offsetof. Или еще какая хитрость. Основная причина наличия offsetof в библиотеке заключается в том, что вы не можете написать его переносимо, вам нужна специальная поддержка со стороны компилятора. - person Bo Persson; 11.04.2011
comment
Как я уже сказал в комментарии выше, offsetof и kin — плохие идеи в C++. Используйте указатель на члены. - person GManNickG; 11.04.2011

Простой ответ заключается в том, что вы не можете. Если тип U является POD, вы можете использовать макрос offsetof, но формально это неопределенное поведение, если тип не является POD: в зависимости от компилятора вы получите ошибку времени компиляции или просто неправильные результаты некоторых из время. И вы не можете использовать его для указателя на член. Вы должны вызвать его с именем класса и именем члена.

person James Kanze    schedule 11.04.2011

Вы можете получить смещение, но это не бесплатно:

template <typename T, class C>
size_t memberOffset(T C::*member)
{
    C c {};
    return size_t(&(c.*member)) - size_t(&c);
}

// usage
struct Vector {
    int x;
    int y;
};

size_t off = memberOffset(&Vector::y);

К сожалению, как вы можете видеть, это не constexpr, и поэтому его нельзя использовать во всех сценариях, которые вам могут понадобиться. У него также есть (очень небольшие) накладные расходы, но кажется, что компилятор просто полностью их оптимизирует: https://godbolt.org/z/jGeox9.

Если вам интересно, можете ли вы просто разбросать constexpr везде самостоятельно и заставить это работать, вы можете, и ваш компилятор может даже скомпилировать его и запустить, но использование приведения к size_t недопустимо в constexpr, несмотря на известный дефект, который допускают многие компиляторы. Это.

Авторы этого метода принадлежат не мне, а Даниэлю Вейлеру и этому превосходному описанию: https://gist.github.com/graphitemaster/494f21190bb2c63c5516

person Asad-ullah Khan    schedule 09.02.2021