2015-06-04 3 views
2

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

Ну, пример, вероятно, будет более понятным.

Проблема для меня: динамическое понижение возвращает 0 во время выполнения в некоторых из следующих случаев (написано в комментариях). Я хотел бы знать, правильное ли поведение (с использованием C++ 11), и почему, и что я могу сделать, чтобы он работал. По-видимому, Templated и A :: A_templated рассматриваются как разные классы, несмотря на то, что они определены как идентичные, используя псевдоним «использование». Проблема не возникает для простого алиаса typedef.

template <class T> 
class Templated {}; 

class A { 
    public : 
    typedef int A_Type; 
    template <class T> 
    using A_Templated = Templated<T>; 
}; 

class Test_base { 
    public : 
    Test_base() {} 
    virtual void foo()=0; 
}; 

template <class T> 
class Test_Type : public Test_base { 
    public : 
    Test_Type() {} 
    void foo() {} 
}; 

template < template <class T> class TT > 
class Test_Templated : public Test_base { 
    public : 
    Test_Templated() {} 
    void foo() {} 
}; 

int main() { 
    Test_base* test; 

    test = new Test_Type<int>; 
    std::cout << dynamic_cast< Test_Type<int>* >(test) << std::endl;//-->ok 
    std::cout << dynamic_cast< Test_Type<A::A_Type>* >(test) << std::endl;//-->ok 

    test = new Test_Templated<Templated>; 
    std::cout << dynamic_cast< Test_Templated<Templated>* >(test) << std::endl;//-->ok 
    std::cout << dynamic_cast< Test_Templated<A::A_Templated>* >(test) << std::endl;//--> returns 0 ! 

    test = new Test_Templated<A::A_Templated>; 
    std::cout << dynamic_cast< Test_Templated<A::A_Templated>* >(test) << std::endl;//-->ok 
    std::cout << dynamic_cast< Test_Templated<Templated>* >(test) << std::endl;//--> returns 0 ! 


} 

Я предлагаю другой способ, чтобы увидеть проблему, это, вероятно, более ясным. Я столкнулся с этим, пытаясь избежать приведенного выше примера. Следующий пример в основном говорит о том, что указал Богдан. Я очень расстраиваю тот факт, что компилятор не может решить Templated with Templated_alias. Мне интересно, существует ли опция компиляции, которая может сортировать разрешение типа силы через псевдонимы шаблонов.

template <class T> 
class Templated {}; 

template <class T> 
using Templated_alias = Templated<T>; 

template < template <class T> class TT > 
class B; 

template <> 
class B<Templated> { 
    public : 
    void foo(Templated<int> _arg) {} 
}; 

int main() { 
    B<Templated> b1; 
    b1.foo(Templated<int>()); 
    b1.foo(Templated_alias<int>());//compiles => Templated_alias<int> is equivalent to Templated<int> 
    B<Templated_alias> b2;//Compilation error: Implicit instantiation of undefined template B<Templated_alias> 
    //which means: Templated_alias is not equivalent to Templated 
} 

Благодаря трюка Богдана, и после того, как какой-нибудь носовым кровотечением, мне удалось найти какое-то решение. Идея заключается в создании класса, отвечающего за «фильтрацию» потенциальных псевдонимов классов шаблонов. Он нуждается в одной спецификации для каждого класса шаблона, который должен быть «отфильтрован». Основным недостатком метода является то, что фильтрация, таким образом, должна использоваться везде, где классы шаблонов используются в качестве параметров шаблона, чтобы быть последовательными.

//Classes to be dealt with 

template <class T> 
class Templated {}; 

template <class T> 
class Templated2 {}; 

template <class T> 
using Templated_alias = Templated<T>; 

class A_base { 
    virtual void foo()=0; 
}; 

template <template <class T> class TT> 
class A : public A_base { 
    void foo() {} 
}; 

//Here starts the trick definition 

template<template<class> class TT1, template<class> class TT2> 
using is_same_template_t = typename std::is_same<TT1<int>, TT2<int> >::type; 

//Template Template aliasing 
template < template <class T> class TT > 
class TT_aliasing { 
    public : 
    template <class T> 
    using Class_T = TT<T>; 
}; 

//Template Template Alias Filtering 
template < template <class T> class TT, class = std::true_type> 
class TT_AF { 
    public : 
    template <class T> 
    using Class_T = TT<T>; 
}; 

template < template <class T> class TT > 
class TT_AF<TT, is_same_template_t<TT, Templated> > : public TT_aliasing<Templated> {}; 

int main() { 

    A_base* a; 
    a = new A< TT_AF<Templated>::Class_T >(); 
    std::cout << dynamic_cast< A< TT_AF<Templated>::Class_T >* >(a) << std::endl; 
    std::cout << dynamic_cast< A< TT_AF<Templated_alias>::Class_T >* >(a) << std::endl; 
    std::cout << dynamic_cast< A< TT_AF<Templated2>::Class_T >* >(a) << std::endl; 

    std::cout << "---------------" << std::endl; 

    a = new A< TT_AF<Templated_alias>::Class_T >(); 
    std::cout << dynamic_cast< A< TT_AF<Templated>::Class_T >* >(a) << std::endl; 
    std::cout << dynamic_cast< A< TT_AF<Templated_alias>::Class_T >* >(a) << std::endl; 
    std::cout << dynamic_cast< A< TT_AF<Templated2>::Class_T >* >(a) << std::endl; 

    std::cout << "---------------" << std::endl; 

    a = new A< TT_AF<Templated2>::Class_T >(); 
    std::cout << dynamic_cast< A< TT_AF<Templated>::Class_T >* >(a) << std::endl; 
    std::cout << dynamic_cast< A< TT_AF<Templated_alias>::Class_T >* >(a) << std::endl; 
    std::cout << dynamic_cast< A< TT_AF<Templated2>::Class_T >* >(a) << std::endl; 

    A< TT_AF<Templated>::Class_T > a1; 
    A< TT_AF<Templated_alias>::Class_T > a2; 
    a1 = a2; 
    A< TT_AF<Templated2>::Class_T > a3; 
    //a1 = a3;//no viable overloaded '=' 

} 

Выход дает:

0x600000014ba0 
0x600000014ba0 
0x0 
--------------- 
0x600000014bb0 
0x600000014bb0 
0x0 
--------------- 
0x0 
0x0 
0x600000014bc0 

После использования вышеуказанной цели. Я столкнулся с разными проблемами. Не может быть абсолютно уверен, что это связано, но это очень вероятно. Компилятор, похоже, пытается правильно построить «динамическую таблицу». Я задал эту проблему на C++ what can make type_info::hash_code differs for two (supposedly) same objects Может быть, я плохой, но на данный момент я бы не рекомендовал использовать трюк с Clang 3.1.

+0

Будьте более ясны относительно _ _ ​​детали. Ошибки времени выполнения, ошибки компилятора? Пожалуйста, разместите их все дословно в своем вопросе. –

+0

сделано редактирование, подумал, что пример достаточно ясен – brahmin

+0

Работы нормально для меня. См. Http://ideone.com/rFTnDd. Какая у вас платформа? –

ответ

3

Поведение Клана верное.

A::A_Type эквивалентен int согласно [7.1.3p1] в стандарте:

[...] В рамках своей декларации, ЬурейеЕ имя-синтаксический эквивалентно ключевое слово и называет тип, связанный с идентификатором способом, описанным в разделе 8. A typedef-name является, таким образом, синонимом для другого типа. A typedef-name не вводит новый тип , как это делает декларация класса (9.1) или декларация перечисления.

A::A_Templated<int> соответствует Templated<int> в соответствии с [14.5.7p2]:

Когда шаблон Идентификатор относится к специализации шаблона псевдонима, это эквивалентно связанного типа, полученного путем замены его шаблонов аргументов для шаблонов параметров в тип-идентификатор шаблон псевдонима.

Однако A::A_Templated является не эквивалентно Templated, согласно [14.5.7p1]:

[...] Имя шаблона псевдонима это имя шаблона.

Это означает, что A::A_Templated и Templated два различных шаблонов, так и Test_Templated<A::A_Templated>Test_Templated<Templated> разные специализации Test_Templated, таким образом, слепки, которые возвращают нулевые указатели правильны в этом.

GCC 5.1.0 не справляется с этим правильно. Clang 3.6.0 и MSVC 14 RC обрабатывают его правильно.


Все ссылки на рабочий чертеж N4431.

Запомните, что активная проблема с активной рабочей группой относительно этого поведения - Issue 1286. Автор говорит, что намерение состоит в том, чтобы ввести стандартную формулировку, чтобы такие дела работали так, как вы ожидали, т. Е. Сделать шаблон псевдонима эквивалентным тому, который указан в идентификатор типа. Там есть записка с мая 2015 года, свидетельствующая о том, что проблема привлекает внимание, но ее пока нет.


С точки зрения «что делает его работу», это трудно дать решения, не зная, что ваши практические потребности, но я бы попытаться сделать Test_Templated зависеть от специализации Templated, а не сам шаблон, который есть, объявить его как

template<class T> 
class Test_Templated : public Test_base { /* ... */ }; 

и использовать его как

test = new Test_Templated<Templated<int>>; 
std::cout << dynamic_cast< Test_Templated<Templated<int>>* >(test) << std::endl; //ok 
std::cout << dynamic_cast< Test_Templated<A::A_Templated<int>>* >(test) << std::endl; //also ok 

Вы можете обернуть это, добавив уровень косвенности, если это помогает я п любым способом:

template<template<class> class TT, class T> using Make_Test_Templated = Test_Templated<TT<T>>; 

, а затем использовать его как это:

test = new Make_Test_Templated<A::A_Templated, long>; 
std::cout << dynamic_cast< Make_Test_Templated<A::A_Templated, long>* >(test) << std::endl; //ok 
std::cout << dynamic_cast< Make_Test_Templated<Templated, long>* >(test) << std::endl; //also ok 

Во всяком случае, я думаю, что ключ, чтобы попытаться использовать тот факт, что специализаций эквивалентны.


Хорошо, основываясь на своем последнем обновлении, вот хак решение проблемы в вашем втором примере кода: изменение явной специализации B<Templated> к частичной специализации, что соответствует только если данный шаблон, который генерирует ту же специализацию, как Templated при создании экземпляра с определенным аргументом (скажем, int для этого примера).

Как это для запутанного предложения? Сожалею. Вот что ваш пример кода становится с вышеупомянутыми изменениями:

#include <iostream> 
#include <type_traits> 

template<class> class Templated { }; 
template<class T> using Templated_alias = Templated<T>; 
template<class> class Templated2 { }; 

// Helper trait 
template<template<class> class TT1, template<class> class TT2> 
using is_same_template_t = typename std::is_same<TT1<int>, TT2<int>>::type; 

template<template<class> class, class = std::true_type> class B; 
template<template<class> class TT> class B<TT, is_same_template_t<TT, Templated>> 
{ 
public: 
    void foo(Templated<int>) { std::cout << "B<Templated>::foo\n"; } 
}; 

int main() { 
    B<Templated> b1; 
    b1.foo(Templated<int>()); 
    b1.foo(Templated_alias<int>()); 
    B<Templated_alias> b2; // Works fine now, and so do the next two lines. 
    b2.foo(Templated<int>()); 
    b2.foo(Templated_alias<int>()); 
    // B<Templated2> b22; // Error trying to instantiate the primary template B. 
} 

Обратите внимание, что вы должны убедиться, что is_same_template_t используется только для проверки шаблонов, которые могут быть созданы с помощью int аргумента (изменить int на то, что вам нужно, конечно,). Если вы хотите, чтобы сделать его более универсальным, вы можете также включать в себя тип, на котором должны быть созданы в списке параметров признака в шаблоны, как это:

template<template<class> class TT1, template<class> class TT2, class T> 
using is_same_template_t = typename std::is_same<TT1<T>, TT2<T>>::type; 

и использовать его как это:

is_same_template_t<TT, Templated, int> 
+0

Так просто, чтобы быть уверенным. Тот факт, что я получаю «возврат 0», является нормальным поведением, поскольку Clang соответствует стандарту, а GCC - нет. Неудовлетворительно. Любая идея для трюка? Даже если у меня возникло ощущение, что мне пришлось пересмотреть мою архитектуру. – brahmin

+0

@ user3033051 Да, это ошибка в GCC, насколько я могу судить. Я не думаю, что поведение должно быть разочаровывающим; Я бы сказал, это на самом деле имеет большой смысл. Для большинства практических применений шаблонов псевдонимов вам понадобятся более сложные конструкции с правой стороны, возможно, с участием шаблонов с разными числами или видами параметров, чем те, которые имеют псевдоним. Попытка установить, в каких случаях может существовать эквивалентность между шаблоном, появляющимся с правой стороны, и шаблон псевдонима сделает некоторые чрезмерно сложные правила без реальной выгоды. – bogdan

+0

Спасибо за это, хорошая идея, но я не могу позволить себе делать это сейчас в своем коде. – brahmin