c++ - gcc的模棱两可的模板实例化错误

标签 c++ gcc clang variadic-templates template-specialization

对于下面的代码,我在 gcc 中得到了一个模棱两可的模板实例化错误。
但是,使用 Clang 或 Visual Studio 代码编译得很好。
可以在此处找到代码的完整工作示例:
http://coliru.stacked-crooked.com/a/60ef9d73ce95e6f9

我有一个从聚合类型构建的类模板

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";
      }
};

第二个特化处理第二个基本列表只有 1 个参数的情况
// 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 ,并且所有示例程序结果在 C++11、C++14 和 C++17 的 GCC 和 Clang 上一致)

我相信 GCC 在这里是错误的,但我一直无法找到相应的(开放的)GCC 错误报告。

[temp.class.order]/1涵盖了类模板特化的偏序 [ 重点 矿]:

For two class template partial specializations, the first is more specialized than the second if, given the following rewrite to two function templates, the first function template is more specialized than the second according to the ordering rules for function templates:

  • (1.1) Each of the two function templates has the same template parameters as the corresponding partial specialization.
  • (1.2) Each function template has a single function parameter whose type is a class template specialization where the template arguments are the corresponding template parameters from the function template for each template argument in the template-argument-list of the simple-template-id of the partial specialization.


因此,为了分析排序,我们将类模板特化重写为函数模板,如上所述:
// 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 [ 重点 矿]:

[temp.func.order]/2

Partial ordering selects which of two function templates is more specialized than the other by transforming each template in turn (see next paragraph) and performing template argument deduction using the function type. The deduction process determines whether one of the templates is more specialized than the other. If so, the more specialized template is the one chosen by the partial ordering process.

[temp.func.order]/3

To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template. [...]

[temp.func.order]/4

Using the transformed function template's function type, perform type deduction against the other template as described in [temp.deduct.partial]. [...]



因此,为两个 f 生成转换后的模板上面的重载,特别是考虑模板模板参数 AggregateType (在两个重载中都使用)以及这些重载的实例化与特定的类模板 Aggregate和类(class) FooBar ,
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

Two sets of types are used to determine the partial ordering. For each of the templates involved there is the original function type and the transformed function type. [...] The deduction process uses the transformed type as the argument template and the original type of the other template as the parameter template.

[temp.deduct.partial]/10

Function template F is at least as specialized as function template G if, for each pair of types used to determine the ordering, the type from F is at least as specialized as the type from G. F is more specialized than G if F is at least as specialized as G and G is not at least as specialized as F.

[temp.deduct.partial]/11

If, after considering the above, function template F is at least as specialized as function template G and vice-versa, and if G has a trailing parameter pack for which F does not have a corresponding parameter, and if F does not have a trailing parameter pack, then F is more specialized than G.



因此,F 至少与 G (/10) 一样专业,而且由于(附加)尾随参数包 Bases2,F 比 G 更专业。存在于 G 但不存在于 F (/11) 中。甚至可以直接应用 [temp.deduct.partial]/10 的第二部分来论证 F 是 更多 专业比G为Aggregate<Foo>Aggregate<Foo, Bases2...>>更专业.

无论哪种方式,每/10 和/11,或仅每/10,Specialized别名
using Specialized = MyClass<Aggregate, Aggregate<Foo, Bar>, Aggregate<Foo>>;

明确地指的是 MyClass 的“第二特化”(来自 OP 帖子) ,特别是重写到上面 F 函数模板的特化,因为这个类模板特化比“第一个特化”(带有附加 Bases2 可变参数模板参数包的那个)更特化。

关于c++ - gcc的模棱两可的模板实例化错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62384512/

相关文章:

c++ - 在 Linux 上使用 MinGW 交叉编译 C++ 代码,目标是 Windows : error happens

c++ - 用于非托管 C++ 的 GDI 对象的调试器可视化工具

linux - Ubuntu 12.04 LTS - 设置新版本的 gcc

c - Apache Avro C 安装

ubuntu - Clang/LLVM header 包含问题(Ubuntu)

c++ - clang 如何设法将这段具有未定义行为的代码编译成这段机器代码?

c++ - 可移植地识别非标准 C++?

c++ - 从input1查找input2中字符的位置

c - 带有 arm64 交叉编译器的 gcc 插件 : cannot open shared object file

arrays - C++ clang 数组比 clang 向量和 gcc 向量和数组快得多