【发布时间】:2015-10-02 08:38:54
【问题描述】:
考虑以下简单(就模板问题而言)示例:
#include <iostream>
template <typename T>
struct identity;
template <>
struct identity<int> {
using type = int;
};
template<typename T> void bar(T, T ) { std::cout << "a\n"; }
template<typename T> void bar(T, typename identity<T>::type) { std::cout << "b\n"; }
int main ()
{
bar(0, 0);
}
clang 和 gcc 都在那里打印“a”。根据[temp.deduct.partial]和[temp.func.order]中的规则,要确定偏序,我们需要合成一些唯一的类型。所以我们有两次推演尝试:
+---+-------------------------------+-------------------------------------------+
| | Parameters | Arguments |
+---+-------------------------------+-------------------------------------------+
| a | T, typename identity<T>::type | UniqueA, UniqueA |
| b | T, T | UniqueB, typename identity<UniqueB>::type |
+---+-------------------------------+-------------------------------------------+
对于“b”的推导,根据Richard Corden's answer,表达式typename identity<UniqueB>::type被视为一个类型并且不被计算。也就是说,这将被合成为:
+---+-------------------------------+--------------------+
| | Parameters | Arguments |
+---+-------------------------------+--------------------+
| a | T, typename identity<T>::type | UniqueA, UniqueA |
| b | T, T | UniqueB, UniqueB_2 |
+---+-------------------------------+--------------------+
很明显,对“b”的推论失败了。这是两种不同的类型,因此您无法将T 推断为两者。
但是,在我看来,A 的扣除应该失败。对于第一个参数,您将匹配 T == UniqueA。第二个参数是一个非推导的上下文——所以如果UniqueA 可以转换为identity<UniqueA>::type,那么推导不会成功吗?后者是替换失败,所以我看不出这个推论怎么会成功。
gcc 和 clang 在这种情况下如何以及为什么更喜欢“a”重载?
【问题讨论】:
-
stackoverflow.com/a/1182688/4326278 包含相关信息,我认为。
-
@bogdan 从将
typename identity<UniqueB>::type视为UniqueB_2的角度来看,这是有道理的。但是为什么对“a”的扣除会成功呢?我稍微改写了这个问题。 -
据我所知,这不仅适用于生成唯一参数,还适用于调整参数:
T, typename identity<T>::type变为T, U,因此推演成功。然而,该算法显然还做了一些标准中未指定的事情。在这种情况下,它“记住”T实际上是在U替换的构造中使用的,因此,在推演过程结束时,如果T尚未从其他地方推演,推演仍然失败总体而言(在 [14.8.2.4p11] 中有提示)。 -
这就是为什么在 the other question 的示例中,仅使用一个参数(
bar(T)与bar(typename identity<T>::type))进行推导失败的原因。正如我所说,整个事情都没有详细说明。 Issue 1391 可能会改变对非推断上下文的处理,并让事情变得更好,尽管还远非完美。 -
附带说明,另一个未指定的情况是为什么
template<typename T> void f(T, T)比template<typename T, typename U> void f(T, U)更专业。标准中没有任何地方说不同类型对的扣除在偏序期间需要保持一致;指定的算法仅单独处理类型对。我的版本:对于函数调用具有参数的参数,使用来自A的发明参数对使用来自P的参数的合成模板声明的合成函数调用必须成功推导。
标签: c++ templates language-lawyer overload-resolution partial-ordering