2013-06-28 4 views
4

В C++ неявно выполняются преобразования типов. Например, объекту типа int можно назначить const int (как это сделано в первых строках основной функции в приведенном ниже коде).Проверка конвертируемости во время выполнения в C++

Теперь я хотел бы проверить конвертируемость во время выполнения в том, что у меня есть структура, в которой я могу добавлять типы, и позже я хочу проверить для данного типа, если один из типов, хранящихся в структуре, будет конвертирован в данный тип.

Вот что я придумал до сих пор:

#include <iostream> 
#include <vector> 

struct bar { virtual void dummy() {} }; 
template<typename T> struct foo : public bar { virtual void dummy() {} }; 

int main() { 

    int i1 = 1; 
    const int i2 = i1; 

    std::vector<bar*> bars; 
    bar* t1 = new foo<int>; bars.push_back(t1); 
    bar* t2 = new foo<int const>; bars.push_back(t2); 

    foo<int const>* t3 = dynamic_cast<foo<int const>*>(bars[0]); 
    std::cout << t3 << std::endl; 

    foo<int const>* t4 = dynamic_cast<foo<int const>*>(bars[1]); 
    std::cout << t4 << std::endl; 

    delete t1; 
    delete t2; 

    return 0; 
} 

Для хранения типов в структуре Я создал шаблонный структуру foo, который является производным от bar. Тогда я могу хранить разные типы int и int const (точнее, указатели на объекты типа foo<int> и foo<int const>) в векторе bar* s. Тогда для данного типа (здесь int const) я проверяю каждый элемент в этом векторе, если он может быть динамически добавлен к foo с этим типом.

При запуске этого кода t3 становится nullptr и t4 указателем, не содержащим нуль. Но я хотел иметь для t3 также ненулевой указатель.

Надеюсь, стало ясно, что я хочу.

Есть ли у вас какие-либо идеи о том, как достичь такой проверки на возможность конвертирования во время выполнения (решение с использованием 11-функций C++ было бы полностью прекрасным)?

+1

Ах, двойную диспетчеризацию. Мы снова встречаемся. –

+0

Если вы знаете и согласны с перечислением всех возможных преобразований во время компиляции, это возможно. В противном случае это невозможно. –

+0

Моя первая реакция была: Использовать 'std :: is_convertible :: value', но по какой-то причине я получаю, что' std :: is_convertible :: value' is true (почему?). – alfC

ответ

5

К сожалению, поскольку foo<int> и foo<const int> - полностью несвязанные типы, вы не можете сделать это легко.

bar* t1 = new foo<int>; 
foo<int const>* t3 = ?????<foo<int const>*>(t1); 

t3 не может быть указателем на любой части t1, так как t1 ни есть, ни содержит, в foo<int const> для t3, чтобы указать на. Единственный способ получить какое-либо хорошее поведение из этого - сделать копию данных, которые t1 содержит совершенно новый foo<int const>*. Это разочаровывающее ограничение является побочным эффектом шаблонов, которые специализируются на несвязанных типах, что является чрезвычайно мощным инструментом, но приводит к этой путанице. Общее правило заключается в том, чтобы НЕ поместить const/volatile квалификацию или ссылки любого типа в параметры шаблона, за исключением того, что это единственная причина для класса шаблона (например,).

Однако, я просто понял, что вы хотите для foo<int> и foo<const int> быть того же типа (МОГ) и что можно сделать (sortof)!

struct base { 
    virtual ~base(){}; //always have virtual destructor with polymorphism 
    virtual void dummy()=0; //pure virtual, forces override 
}; 
template<typename T> 
struct foo : public bar { 
    virtual void dummy() {} 
}; 
template<typename T> struct foo<const T> : public foo<T> {}; 
template<typename T> struct foo<volatile T> : public foo<T> {}; 
template<typename T> struct foo<const volatile T> : public foo<T> {}; 


base* t1 = new derived<const int>; 
derived<int>* t3 = dynamic_cast<derived<int>*>(t1); //hooray! 
//you can go from derived<const int> to derived<int> but not the other way around 
+0

Это выглядит интересно. В вашем коде он поддерживает только cv-квалификаторы, но я думаю, что могу расширить его, по крайней мере, до более крупного подмножества стандартных преобразований типов. Правильно ли, что третья строка с константой volatile на самом деле не нужна, так как иначе мы бы просто имели двухэтапное наследование a la 'foo является foo является foo (является базой)'? – mg84

+0

Хотя я не делаю именно то, что я надеялся, я принимаю это как принятый ответ. Кажется (в расширенной версии) быть ближе, чем я могу получить. – mg84

-1

Использование std::is_convertible<From*, To*>::value

#include<type_traits> 
int main(){ 
    using namespace std; 
    cout << std::is_convertible<int const, int>::value << endl; // print true 
    cout << std::is_convertible<int const*, int*>::value << endl; // print false 
    cout << std::is_convertible<int const, int>::value << endl; // print true 
    cout << std::is_convertible<std::string, int>::value << endl; // print false 
    cout << std::is_convertible<std::string*, int*>::value << endl; // print false 
} 

Обратите внимание, что вы должны использовать тип указателя, чтобы получить Освобожденные поведения по отношению к const Нессам, например.

(я предполагаю, что в C++ 98 вы можете сделать то же самое с boost::is_convertible.)

+0

И как вы примените это к полиморфному шаблону, как в вопросе? –

+0

Ну, я не понял эту часть вопроса, я предположил, что, как только у вас будет доступ к конверсии во время выполнения и времени компиляции, вы можете делать с ней все, что хотите, включая отправку, двойную отправку и отправку времени компиляции, enable_if' и т. д. – alfC

+0

Конверсии _easy_. Он хочет для 'foo ' и 'foo ' для того же типа. –