对于下面的代码,我在gcc中收到模棱两可的模板实例化错误。但是,使用Clang或Visual Studio可以很好地编译代码。完整的代码示例可以在这里找到:http://coliru.stacked-crooked.com/a/60ef9d73ce95e6f9
我有一个从Aggegate类型构建的类模板
template<template<typename...> typename AggregateType, typename ...>
struct MyClass;
聚合类型由基类列表组成,例如
template<typename ... Bases>
struct Aggregate : Bases...
{ };
我已经定义了MyClass的两个专业。第一个专业是常见情况,请阅读
// specialization for two argument list for the
// aggregate type
template<template<typename...> typename AggregateType,
typename Base,
typename ... Bases1,
typename ... Bases2>
struct MyClass<
AggregateType,
AggregateType<Bases1...>,
AggregateType<Base, Bases2...>>
{
void func()
{
std::cout << "not specialized\n";
}
};
第二个专业化处理第二个基本列表只有一个参数时的情况
// specialization for the second argument list with length 1
template<template<typename...> typename AggregateType,
typename Base,
typename ... Bases1>
struct MyClass<
AggregateType,
AggregateType<Bases1...>,
AggregateType<Base>>
{
void func()
{
std::cout << "specialized\n";
}
};
使用带有长度为1的第二个参数列表的MyClass,我希望编译器选择MyClass的第二个特殊化,因为它是更专门的模板
class Foo {};
class Bar {};
int main()
{
// this should give the not specialized class
using NotSpecialized = MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo, Bar>>;
NotSpecialized ns;
ns.func();
// this should give the specialized class
using Specialized = MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo>>;
Specialized s;
s.func();
}
虽然代码可以在Clang上正常工作,但gcc给出了模棱两可的模板实例化错误。我该如何避免此错误并仍然使用gcc?如果删除AggregateType模板参数,该代码也可用于gcc,请参见http://coliru.stacked-crooked.com/a/c1f6edd5fab7df4d
(以下所有ISO标准参考均引用N4659: March 2017 post-Kona working draft/C++17 DIS,并且所有示例程序结果在GCC和Clang上对于C ++ 11,C ++ 14和C ++ 17都是一致的)]] >> 我相信这里的GCC是错误的,但我无法为其找到公开的GCC错误报告。
[[temp.class.order]/1涵盖了类模板专业的部分排序[[[重点
我的]] >>对于两个类模板的局部专业,第一个是 如果给定以下。 因此,为了分析顺序,我们按照上面的方法将类模板专业化重写为函数模板:重写为两个 功能模板
,第一个功能模板更专业 比第二个根据ordering rules for function templates:((1.1)这两个功能模板中的每一个都具有与相应的局部专业化相同的模板参数。
(1.2)每个函数模板都有一个函数参数,其类型是类模板特化,其中模板参数 是功能模板中的相应模板参数 对于<中的每个模板参数 部分专业化的 simple-template-id
// G)
template<template<typename...> typename AggregateType,
typename Base,
typename... Bases1,
typename... Bases2>
void f(MyClass<AggregateType,
AggregateType<Bases1...>,
AggregateType<Base, Bases2...>>);
// F)
template<template<typename...> typename AggregateType,
typename Base,
typename... Bases1>
void f(MyClass<AggregateType, AggregateType<Bases1...>, AggregateType<Base>>);
f
的G和F重载的部分排序由[temp.func.order]/2,[temp.func.order]/3和[temp.func.order]/4 [
强调矿山控制:]。推导过程确定是否 其中一个模板比另一个模板更专业。如果是这样, 更专业的模板是部分排序选择的模板 过程。 [临时命令] / 3[临时功能] / 2
偏序选择两个功能模板中的哪个更大 比其他通过依次转换每个模板] (请参阅下一段)
并执行模板参数推导 使用函数类型
模板模板参数
(包括模板参数包 其中的<类型,值或类模板 分别并将其替换为该参数的每次出现 在模板的功能类型中。 [...] [临时功能] / 4 使用变换后的功能模板的功能类型,如[temp.deduct.partial]中所述,对另一个模板执行类型推导。 [...]因此,要为上面的两个f
重载生成转换后的模板,尤其要考虑模板模板参数AggregateType
(在两个重载中都使用)以及使用特定类模板Aggregate
和类实例化这些重载Foo
和Bar
,template<typename ... Bases>
struct Aggregate : Bases...
{ };
class Foo {};
class Bar {};
用作模板模板参数,在继续分析原始类模板的部分排序时,我们可以不失一般性地将以下(部分)转换后的函数模板视为参数模板:
// G-transformed (argument template>
template<typename... Bases2>
void f(MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo, Bases2...>>);
// F-transformed (argument template>
void f(MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo>>);
从[temp.deduct.partial]/2,[temp.deduct.partial]/10和[temp.deduct.partial]/11 [摘录,重点我的]:
两组类型用于确定部分排序。对于每个 涉及的模板中有原始功能类型和 转换后的函数类型。 [...]扣除过程使用[temp.deduct.partial] / 2
转换后的类型作为参数模板和另一个模板作为参数模板。
[temp.deduct.partial] / 10
如果对于用于确定排序的每一对类型,F中的类型至少与G中的类型一样专业,那么F [功能模板F至少与功能模板G一样专业。F比G更专业。如果F至少与G一样专业,而G至少与F不一样专业。如果考虑了上述内容,则功能模板F至少与功能模板G一样专业,反之亦然,如果G 具有尾随参数包
F没有尾随参数包,则F比G更专业。
由此得出,F是至少的G(/ 10)专业化对象,此外,由于G中存在(附加)尾随参数包Bases2
,因此F比G更专业。不在F(/ 11)中。因此,Specialized
别名using Specialized = MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo>>;
MyClass
的“第二个专业化”(摘自OPs post),特别是改写到上面F函数模板的专业化,因为该类模板的专业化比“第一个专业化” “(带有附加的Bases2
可变参数模板参数的那个。)>