c++ - C++ dynamic模板模板参数为类模板或者别名模板的类模板

  显示原文与译文双语对照的内容
107 0

我希望这个标题有意义。我可能漏掉了词汇来正确表达它。

举个例子也许更清楚一点。

我的问题是:动态向下返回在下列情况下,在运行时返回 0.我想知道它是否是正确的行为( 使用 C++11 ),还有为什么我能做到这一点。显然,模板化和 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!
}

我建议另一种方法来发现这个问题,这可能更清楚。我试图避免上面的例子。下面的示例说明Bogdan指出了什么。我发现编译器不能用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
}

由于bogdan的技巧,一些小鼻鼻血后,我找到了一些解决方案。它的思想是构建一个负责'筛选'模板类的潜在别名的类。需要每个模板类的一个规范,需要为'已经筛选'。方法的主要缺点是需要在任何地方使用筛选,以便模板类作为模板参数。

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

使用上述技巧后。我遇到了不同的问题。不能绝对确定它是相关的但很有可能。编译器似乎难以正确地构建'动态表格'。我们可以将两个( 预计) 相同的对象( 可能是我的坏对象) differs,但是现在我不建议使用 Clang,但现在我不建议使用 Clang 。

时间:原作者:0个回答

148 2

clang的行为正确。

A::A_Type 按照标准中的[7.1.3p1] 等价于 int:

在声明范围内,与 keyword typedef相同的名称在语法上等效于关键字,并以 Clause 8描述的方式命名与标识符关联的类型。因此,typedef名称是另一个类型的同义词。类声明名不引入类声明( 9.1 ) 或者 enum 声明的新类型。

根据 [14.5.7p2],A::A_Templated<int> 等效于 Templated<int>:

模板标识引用别名模板 equivalent equivalent模板参数中的模板参数substitution的substitution模板参数 equivalent equivalent 。

但是,根据 [14.5.7p1],A::A_Templated的值与 Templated的值不一致,这与相同:

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

这意味着 A::A_TemplatedTemplated 是两个不同的模板,所以Test_Templated<A::A_Templated>而且 Test_Templated<Templated>Test_Templated的专门化是不同的,因此返回空指针的转换是正确的。

GCC 5.1.0不正确处理这里问题。Clang 3.6.0和 MSVC 14 RC正确处理。

所有引用均为工作草案 N4431.

注意,有一个活跃的核心工作组问题认为这个行为是问题 1286.目的是引入标准的措辞,使这种情况按照你预期的方式工作,即使别名模板等价于类型 id 中引用的alias 。在 2015年05月 中有一个注释,表明这个问题正在接收,但它还没有在那里。

在"使它工作"中,不知道你的实际需求是什么,但是我想让 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

无论如何,我认为关键是要尝试使用专门化的specializations 。

好的,根据最新的更新,下面是一个 hack,用于解决第二个代码示例中的问题:如果给定模板生成相同专用化的模板,则将显式专用化 B<Templated> 更改为只包含 MATCHES的部分专用化。

一个混乱的句子?抱歉,下面的更改将显示你的代码示例:

#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>::foon"; }
};
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>
原作者:
...