C ++动态向下转换到类模板,模板模板参数是类模板或别名模板 [英] C++ dynamic downcasting to class template having template template parameter being a class template or an alias template

查看:125
本文介绍了C ++动态向下转换到类模板,模板模板参数是类模板或别名模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望标题有意义。



很好,一个例子可能会更清楚。



对我来说是:动态downcasting在运行时在以下一些情况(写在评论中)返回0。我想知道如果它是一个正确的行为(使用C ++ 11),也为什么,我该怎么做,使其工作。显然,Templated和A :: A_templated被视为不同的类,尽管使用别名using定义为相同。对于简单的typedef别名,不会出现问题。

 模板< class T& 
class Templated {};

class A {
public:
typedef int A_Type;
template< class T>
使用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>类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; // - >返回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; // - >返回0!


}






我建议另一种方式来看问题,这可能更清楚。我试图避免上面的例子面对它。下面的例子基本上说Bogdan指出。我发现非常令人沮丧的事实,编译器不能解决与Templated_alias的模板。我想知道是否存在一个编译选项,它可以通过模板别名来排序强制类型解析。

  template< class T> ; 
类Templated {};

template< class T>
使用Templated_alias = Templated< T> ;;

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

模板<>
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>相当于Templated< int>
B< Templated_alias> b2; //编译错误:未定义的模板B的隐式实例化< Templated_alias>
//表示:Templated_alias不等同于模板
}






感谢Bogdan的窍门,经过一些小鼻子出血,我设法找到了一些解决方案。这个想法是建立一个负责过滤模板类别的潜在别名的类。它需要每个模板类一个规范需要过滤。该方法的主要缺点是过滤因此需要使用模板类作为模板参数的地方,以便一致。

  //要处理的类

template< class T>
class Templated {};

template< class T>
class Templated2 {};

template< class T>
使用Templated_alias = Templated< T> ;;

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

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

//这里开始窍门定义

template< template< class>类TT1,模板< class>类别TT2>
使用is_same_template_t = typename std :: is_same< TT1< int>,TT2< int> > :: type;

//模板模板别名
template< template< class T>类TT>
class TT_aliasing {
public:
template< class T>
使用Class_T = TT< T>
};

//模板模板别名过滤
模板< template< class T> class TT,class = std :: true_type>
class TT_AF {
public:
template< class T>
使用Class_T = TT< T>
};

template< template< class T>类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; //没有可行的重载'='

}


b $ b

输出:

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






使用上面的技巧。我遇到了不同的问题。不能绝对确定它是相关的,但它很可能。编译器似乎很难正确建立动态表。我在要求解决此问题C ++可以使type_info :: hash_code对于两个(据说)相同的对象不同
可能是我的坏,但现在我不建议使用Clang 3.1的技巧。

解决方案

Clang的行为是正确的。



A :: A_Type 相当于标准中的[7.1.3p1]的 int


[...]在其声明的范围内, typedef-name 在语法上为
,相当于一个关键字,并命名与
相关联的类型标识符。因此, typedef-name 是另一个类型的
同义词。




A :: A_Templated< int> 等价于 Templated< int& 14.5.7p2]:


template-id 引用别名模板的专业化时,
它等价于通过用 template-arguments 替换 type-id 中的模板参数 em>
别名模板。


但是, A :: A_Templated 等效于 Templated



< blockquote>

[...]别名模板的名称是一个模板名称


这意味着 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 。作者说,目的是引入标准的措辞,使这种情况按照你所期望的方式工作,也就是说,使别名模板等同于在 type-id 中引用的模板。 2015年5月有一条注释,表示该问题正在受到关注,但尚未出现。






术语使它工作,很难在不知道你的实际需要是什么提供解决方案,但我会尝试使 Test_Templated 依赖于模板,而不是模板本身,也就是说,

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

并使用

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

你可以通过添加一个间接级别来包装:

 模板< template< class>类TT,类T>使用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

无论如何,我认为关键是尝试使用 是等同的。

好的,根据你最新的更新,这里是一个hack解决问题您的第二个代码示例:将显式专门化 B 更改为部分特化,只有在给定一个生成与 当使用某个参数实例化时(对于此示例,我们说 int )。



这是一个混乱的句子怎么样?抱歉。这里是你的代码示例成为与上述变化:

  #include< iostream& 
#include< type_traits>

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

//帮助器trait
模板< template< class>类TT1,模板< class>类别TT2>
使用is_same_template_t = typename std :: is_same< TT1< int>,TT2< int>

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

int main(){
B< Templated> b1;
b1.foo(Templated< int>());
b1.foo(Templated_alias< int>());
B< Templated_alias> b2; //现在工作得好,接下来的两行也是这样。
b2.foo(Templated< int>());
b2.foo(Templated_alias< int>());
// B< Templated2> b22; //错误尝试实例化主模板B.
}

请注意,确保 is_same_template_t 仅用于检查可以使用 int 参数实例化的模板(更改 int 到任何你需要的,当然)。如果你想让它更通用,你还可以包括模板需要在trait的参数列表中实例化的类型,例如:

  template< template< class>类TT1,模板< class>类别TT2,类别T> 
使用is_same_template_t = typename std :: is_same< TT1< T>,TT2>> :: type;

并使用它:

  is_same_template_t< TT,Templated,int> 


I hope the title makes sense. I probably miss vocabulary to express it correctly.

Well, an exemple will probably be more clear.

Problem for me is: dynamic downcasting returns 0 at run time in some of the following cases (written in comments). I'd like to know if it's a correct behaviour (using C++11), also why, and what can I do to make it work. Apparently, Templated and A::A_templated are treated as different classes, despite being defined as identical by using alias "using". Problem doesn't appear for simple typedef alias.

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 !


}


I propose another way to see the problem, this is probably more clear. I'm facing it after trying to avoid the example above. The following example's basically says what Bogdan pointed out. I find very frustrating the fact the compiler can't resolve Templated with Templated_alias. I'm wondering if a compilation option exists, which can sort of force type resolving through template aliases.

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
}


Thanks to Bogdan's trick, and after some little nose-bleeding, I managed to find some kind of solution. The idea is to build a class in charge of 'filtering' potential aliases of template classes. It needs one specification per template class needed to be 'filtered'. Main drawback of the method is that filtering thus needs to be used everywhere template classes are used as template parameters in order to be consistent.

//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 '='

}

Output gives:

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


After using the above trick. I ran into different problems. Can't be absolutely sure it's related but it's very likely. Compiler seems to struggle to correctly build the 'dynamic table'. I asked for this problem at C++ what can make type_info::hash_code differs for two (supposedly) same objects May be my bad, but for now I wouldn't recommend using the trick with Clang 3.1 .

解决方案

Clang's behaviour is correct.

A::A_Type is equivalent to int according to [7.1.3p1] in the standard:

[...] Within the scope of its declaration, a typedef-name is syntactically equivalent to a keyword and names the type associated with the identifier in the way described in Clause 8. A typedef-name is thus a synonym for another type. A typedef-name does not introduce a new type the way a class declaration (9.1) or enum declaration does.

A::A_Templated<int> is equivalent to Templated<int> according to [14.5.7p2]:

When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.

However, A::A_Templated is not equivalent to Templated, according to [14.5.7p1]:

[...] The name of the alias template is a template-name.

This means that A::A_Templated and Templated are two different templates, so Test_Templated<A::A_Templated> and Test_Templated<Templated> are different specializations of Test_Templated, thus the casts that return null pointers are correct in doing so.

GCC 5.1.0 doesn't handle this correctly. Clang 3.6.0 and MSVC 14 RC handle it correctly.


All references are to working draft N4431.

Note that there is an active Core Working Group Issue regarding this behaviour - Issue 1286. The author says that the intention is to introduce standard wording to make such cases work the way you expected, that is, make the alias template equivalent to the one referenced in the type-id. There's a note from May 2015 in there, indicating that the issue is receiving attention, but it's not there yet.


In terms of "making it work", it's difficult to give solutions without knowing what your practical needs are, but I'd try to make Test_Templated depend on specializations of Templated, rather than the template itself, that is, declare it like

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

and use it like

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

You could wrap this by adding a level of indirection, if that helps in any way:

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

and then use it like this:

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

Anyway, I think the key is to try to use the fact that the specializations are equivalent.


Alright, based on your latest update, here's a hack addressing the problem in your second code sample: change the explicit specialization B<Templated> to a partial specialization that only matches if given a template that generates the same specialization as Templated when instantiated with a certain argument (let's say int for this example).

How's that for a confusing sentence? Sorry. Here's what your code sample becomes with the above changes:

#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.
}

Note that you have to make sure is_same_template_t is only used to check templates that can be instantiated with an int argument (change int to whatever you need, of course). If you want to make it more generic, you can also include the type on which the templates need to be instantiated in the trait's parameter list, like this:

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;

and use it like this:

is_same_template_t<TT, Templated, int>

这篇关于C ++动态向下转换到类模板,模板模板参数是类模板或别名模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆