【问题标题】:Variadic template as template parameter: deduction works with GCC but not with Clang可变参数模板作为模板参数:推导适用于 GCC,但不适用于 Clang
【发布时间】:2012-11-11 08:25:39
【问题描述】:

在使用 GCC 4.7.2 和 Clang 3.1 编译一些 C++11 代码时,我遇到了一个问题,即 Clang 无法推断出 GCC 成功的模板参数。 在更抽象的形式中,代码如下所示:

src/test.cc:

struct Element {
};

template <typename T>
struct FirstContainer {
};

template <typename T, typename U = Element>
struct SecondContainer {
};

template <template <typename> class Container>
void processOrdinary(Container<Element> /*elements*/) {
}

template <template <typename, typename> class Container>
void processOrdinary(Container<Element, Element> /*elements*/) {
}

template <template <typename, typename...> class Container>
void processVariadic(Container<Element> /*elements*/) {
}

int main() {
  // This function instantiation works in both GCC and Clang.
  processOrdinary(FirstContainer<Element>{});
  // This function instantiation works in both GCC and Clang.
  processOrdinary(SecondContainer<Element>{});
  // This function instantiation works in both GCC and Clang.
  processVariadic(FirstContainer<Element>{});
  // This function instantiation works in both GCC and Clang.
  processVariadic<SecondContainer>(SecondContainer<Element>{});
  // This function instantiation works in GCC but not in Clang.
  processVariadic(SecondContainer<Element>{});
  return 0;
}

通过阅读第 14.3.3 节中的示例和标准第 14.8.2 节中的规范,我认为推论应该有效,但我不能肯定地说。这是我从构建中得到的输出:

mkdir -p build-gcc/
g++ -std=c++0x -W -Wall -Wextra -Weffc++ -pedantic -c -o build-gcc/test.o src/test.cc
g++  -o build-gcc/test build-gcc/test.o
mkdir -p build-clang/
clang++ -std=c++11 -Weverything -Wno-c++98-compat -c -o build-clang/test.o src/test.cc
src/test.cc:34:3: error: no matching function for call to 'processVariadic'
  processVariadic(SecondContainer<Element>{});
  ^~~~~~~~~~~~~~~
src/test.cc:21:6: note: candidate template ignored: failed template argument deduction
void processVariadic(Container<Element> /*elements*/) {
     ^
1 error generated.
make: *** [build-clang/test.o] Fel 1

为什么结果不同? GCC 是马虎、Clang 笨,我的代码是否包含未指定的行为或全部?

【问题讨论】:

  • 我同意你的看法。我在 C++11 最终草案中看到的所有内容都表明这应该可行。 14.3.3.3 尤其相关。
  • 您的示例缺少typedef int Element;,对吗?
  • 不,在代码的开头我定义了一个名为 Element 的结构。
  • 您是否尝试将其发布到 clang/llvm 邮件列表?他们可能对此有更深入的了解,并且很高兴知道他们的实施是否不完整。

标签: c++ gcc c++11 clang variadic-templates


【解决方案1】:

Clang 正在尝试推断此调用的参数:

processVariadic(SecondContainer<Element>{});

由于SecondContainer 有一个默认模板参数,这相当于:

processVariadic(SecondContainer<Element, Element>{});

因此,它使用P = Container&lt;Element&gt;A = SecondContainer&lt;Element, Element&gt; 执行模板参数推导。它可以立即推断出Container模板参数是SecondContainer

接下来,它会考虑模板参数。由于参数类型是完全解析的,Clang 认为参数必须有尽可能多的类型,否则推导不可能成功(它不考虑默认参数)。所以它标记了一个扣除失败。


那么,应该发生什么?用[temp.deduct.type]p8的话来说,

如果PA具有以下形式之一,则可以推导出模板类型参数T、模板模板参数TT或模板非类型参数i:
[...]
TT&lt;T&gt;
TT&lt;i&gt;
TT&lt;&gt;
其中 [...] &lt;T&gt; 表示模板参数列表,其中至少一个参数包含 T&lt;i&gt; 表示模板参数列表,其中至少一个参数包含 i&lt;&gt; 表示模板参数列表,其中没有参数包含 Ti

为了匹配模板参数,我们转向[temp.deduct.type]p9

如果P 有一个包含&lt;T&gt;&lt;i&gt; 的形式,则将相应模板参数列表P 的每个参数Pi 与相应模板参数列表的相应参数Ai 进行比较A.

这里有两件事要注意。一个是该规则没有说明如果列表PiAi 的长度不同(在这种情况下就是这样)会发生什么,并且常见的解释似乎是不检查不匹配的项目。另一个是无论如何都不应该遵循这个规则,因为P的形式不包含&lt;T&gt;&lt;i&gt;(它只包含&lt;&gt;,因为其中没有模板参数)。


因此,Clang 拒绝此代码是错误的。我已经在r169475 中修复了它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-27
    • 2016-10-05
    相关资源
    最近更新 更多