#c #templates #member-pointers
#c #шаблоны #указатели на элементы
Вопрос:
template<class T, typename U> ptrdiff_t foo(T U::* m)
{
// return offset
}
Как я могу получить смещение поля ‘m’ в этом контексте? Я бы предпочел использовать выражение во время компиляции.
Заранее спасибо за любую помощь. С наилучшими пожеланиями
Комментарии:
1. Вероятно, это плохая идея. Почему бы вместо этого не использовать тип указателя на элемент?
2. Хм, что именно ты имеешь в виду? Приведенный выше параметр ЯВЛЯЕТСЯ указателем на элемент …
3. Правильно, и оставьте все как есть. Смещения просто слишком примитивны, чтобы использоваться для большинства классов C (не POD).
4. Хорошо, но вы написали ‘почему бы вместо этого не использовать тип указателя на элемент? Что вы это имеете в виду? Кстати.: Я мог бы написать специфичную для компилятора версию моей функции member_offset.
5. @GManNickG Vulkan, OpenGL и любые другие графические библиотеки, использующие структурированные буферы, запрашивают смещения вместо указателей на элемент. В этом случае они не понимают указатели на элемент, и вы должны перевести это в смещение.
Ответ №1:
@Michael J
Спасибо за ваш ответ. Это было не совсем то, что я искал, но это вдохновило меня на это:
template<class T, typename U>
std::ptrdiff_t member_offset(U T::* member)
{
return reinterpret_cast<std::ptrdiff_t>(
amp;(reinterpret_cast<T const volatile*>(NULL)->*member)
);
}
Комментарии:
1. Здесь вы разыменовываете нулевой указатель. Это недопустимо, если только вы не внедряете библиотеку и не получаете специальное разрешение на использование в макросе offsetof. Кто-то опередил меня с -1.
2. Вот что делает макрос offsetof: (size_t)amp;reinterpret_cast<постоянный изменяемый символamp;>((((s *)0)->m)). В чем разница?
3. Разница в том, что offsetof делает это для отдельного компилятора , и только потому, что разработчик библиотеки заключил специальную сделку с автором компилятора. Тогда это могло бы сработать. В других компиляторах вместо offsetof используется специальная __встроенная_функция offsetof. Или какой-нибудь другой трюк. Основная причина наличия offsetof в библиотеке заключается в том, что вы не можете записать ее переносимо, вам нужна специальная поддержка со стороны компилятора.
4. Как я уже говорил в комментарии выше,
offsetof
и kin — плохие идеи в C . Используйте указатель на элементы.
Ответ №2:
Звучит так, как будто вы ищете макрос offsetof().
Комментарии:
1. Недопустимо вызывать
offsetof
с указателем на элемент, только с именем элемента. (во многих реализациях это может сработать, но, учитывая, насколько неясны такого рода вещи, это может привести к сбою при любых обновлениях компилятора)2. Привет @Yakk. Я не совсем уверен, что вы имеете в виду. Если вы нажмете на слово «offsetof» (выше), это приведет вас к документам для макроса. Мне это кажется кошерным. Под «указателем на элемент» вы подразумеваете элемент структуры, который является указателем? например, struct s { int i; char *p; }; size_t n = offsetof(struct s, p);
3. Вы не можете использовать
offsetof()
, если указатель на элемент является аргументом шаблона.
Ответ №3:
Вы можете получить смещение, но это не бесплатно:
template <typename T, class C>
size_t memberOffset(T C::*member)
{
C c {};
return size_t(amp;(c.*member)) - size_t(amp;c);
}
// usage
struct Vector {
int x;
int y;
};
size_t off = memberOffset(amp;Vector::y);
К сожалению, как вы можете видеть, это не constexpr
, и поэтому его нельзя использовать во всех сценариях, которые вы можете захотеть. Он также имеет (очень маленькие) накладные расходы, но, похоже, компилятор просто полностью оптимизирует его: https://godbolt.org/z/jGeox9 .
Если вам интересно, можете ли вы просто разбрызгать constexpr
везде самостоятельно и заставить это работать, вы можете, и ваш компилятор мог бы даже скомпилировать его и запустить, но использование приведения к size_t
недопустимо в constexpr
несмотря на известный дефект, который допускают многие компиляторы.
Заслуги за этот метод принадлежат не мне, а Дэниелу Вейлеру и его превосходной сути: https://gist.github.com/graphitemaster/494f21190bb2c63c5516
Ответ №4:
Простой ответ заключается в том, что вы не можете. Если тип U является POD , вы можете использовать макрос offsetof
, но формально, это неопределенное поведение, если тип не является POD: в зависимости от компилятора, вы получите ошибку во время компиляции или просто неправильные результаты иногда. И вы не можете использовать это для указателя на элемент. Вы должны вызвать его с именем класса и именем элемента.