2017-01-30 14 views
0

Часто в коде я пишу, что есть типы, совместимые с макетами, но разные типы, но все же я бы хотел их обойти, как если бы они были того же типа. Это приходит с довольно синтаксическими накладными расходами, в том числе необходимых слепков и т.д.Насколько совместим с нормами мой полиморф?

Я (очень) недавно придумал небольшие вспомогательный микс в классе, который я дублировал polymorph:

struct polymorph 
{ 
    template<typename T> 
    const T& as() const { return reinterpret_cast<const T&>(*this); } 
    template<typename T> 
    T& as() { return reinterpret_cast<T&>(*this); } 
} 

Небольшого примера «демонстрирует «это для классической« мой сложный номер типа лучше вашей »ситуации here *. Мой вопрос: насколько я уверен в этом классе и как я могу сделать это более устойчивым к неправильному использованию и/или неопределенному поведению. Я не использовал его много, и я колеблюсь, потому что есть много вещей, которые могут пойти ужасно неправильно.

Этот класс в основном предназначен для соответствий, таких как между _Complex/std::complex<double>/double[2]. Я все еще думаю о хорошем способе продлить его, чтобы выполнить полезные преобразования, и насколько это было бы полезно.

* Примечание. Я не говорю, что это абсолютно не определено - поведение бесплатное. Отсюда этот вопрос.

+2

Это похоже на то, что вы хотите ['std :: variant'] (http://en.cppreference.com/w/cpp/utility/variant) – NathanOliver

+0

@Nathan Я явно не хочу список ограниченного типа. – rubenvb

+0

Что касается 'complex ' <-> 'float/double [2]', это хорошо определено, потому что стандарт говорит об этом. В противном случае я считаю, что такой тип пиннинга подходит только для союзов, где типы членов имеют общие начальные последовательности. – Praetorian

ответ

2

Вы нарушаете [basic.lval]/8, обычно называемый «строгим правилом псевдонимов». В нем говорится, что вы не можете получить доступ к объекту с помощью указателя типа, отличного от объекта, на который указана, с рядом исключений (указатели на базовые классы, unsigned char*, const/volatile отличия и т. Д.). И совместимость макетов не является частью этого рассмотрения.

Так что да, это UB.

+2

OP хочет знать, как расширить его, чтобы сделать его более надежным, чтобы класс не мог использоваться для запуска неопределенного поведения. Он использует его в контексте строгого правила псевдонимов. Он знает, что его можно использовать, чтобы его использовать, он просто просит продлить его или дать нам подсказки о том, как его расширить, чтобы блокировать использование, которое приведет к неопределенному поведению. – johnathon

+0

@johnathon: Как сказал MSalters в своем комментарии , * единственный случай *, который не является UB, является самопереходом, что делает его функционально бесполезным. Вам не разрешается делать вид, что один объект является другим объектом, каким бы «совместимым» он не казался. Период. Единственное, что вы можете сделать, это * конвертировать * объект в другой, путем инициализации нового объекта в другом хранилище. –

+0

Вы утверждаете, что 'struct A {int x;}; A a; ((INT *) &a)=0; 'является UB Как' структура B {INT у}; ((B *) и а) -> у = 0; ' – Yakk