Отместване на указател към член

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
Добре, но бяхте написали „защо не използвате тип указател към член ВМЕСТО? Как го имаш предвид? Btw.: Бих могъл да напиша специфична за компилатора версия на моята функция 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 (по-горе), ще ви отведе до документите за макроса. Изглежда ми кошер. Под указател на член имате предвид член на структура, която е указател? напр. struct s { int i; char *p; }; 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 въпреки известния дефект, който много компилатори позволяват то.

Заслугите за този метод не принадлежат на мен, а на Daniel Weiler и тази отлична същност: https://gist.github.com/graphitemaster/494f21190bb2c63c5516

person Asad-ullah Khan    schedule 09.02.2021