Это немного эзотерический вопрос, но мне было любопытно, является ли закономерным следующий шаблон расширения класса (как, например, UB) в современном C ++ (для всех намерений и целей я в порядке, ограничив обсуждение C ++17 и позже).
template<typename T>
struct AddOne {
T add_one() const {
T const& tref = *reinterpret_cast<T const*>(this);
return tref + 1;
}
};
template<template<typename> typename E, typename T>
E<T> const& as(T const& obj) {
return reinterpret_cast<E<T> const&>(obj);
}
auto test(float x) {
return as<AddOne>(x).add_one();
}
auto test1(int x) {
return as<AddOne>(x).add_one();
}
// a main() to make this an MVCE
// will return with the exit code 16
int main(int argc, const char * argv[]) {
return test1(15);
}
Приведенный выше код является полным примером, он компилирует, запускает и выдает ожидаемый результат, по крайней мере, clang в режиме C ++ 17.Проверьте код разборки в проводнике компилятора: https://godbolt.org/z/S3ZX2Y
Моя интерпретация следующая: стандарт гласит, что reinterpret_cast
может конвертировать между указателями / ссылками любых типов, но при обращении к ним получающиеся ссылки могут быть UB (согласно правилам алиасинга).В то же время преобразование полученного значения обратно в исходный тип гарантирует получение исходного значения.
Исходя из этого, просто повторная ссылка на float
как ссылка на AddOne<float>
не вызывает UB.Поскольку мы никогда не пытаемся получить доступ к какой-либо памяти за этой ссылкой как к экземпляру AddOne<float>
, здесь также нет UB.Мы используем только информацию о типе этой ссылки, чтобы выбрать правильную реализацию add_one()
функции-члена.Сама функция скрывает ссылку на исходный тип, поэтому, опять же, нет UB.По сути, этот шаблон семантически эквивалентен этому:
template<typename T>
struct AddOne {
static T add_one(T const& x) {
return x + 1;
}
};
auto test(float x) {
return AddOne<Int>::add_one(x);
}
Я прав или есть что-то, что я здесь упускаю?
Считайте это академическим упражнением при изучении стандарта C ++.
Редактировать: это не дубликат Когда использовать reinterpret_cast? , поскольку в этом вопросе не обсуждается приведение указателя this
или использование reinterpret_cast
для отправки по переинтерпретированному типу.