【发布时间】:2019-12-02 02:30:48
【问题描述】:
考虑以下示例 (Coliru link):
template <class... T> struct S { using type = int; };
template <class... T>
void f(typename S<T...>::type) {
static_assert(sizeof...(T) == 0);
}
template <class... T>
void g(typename S<T...>::type, S<T...>*) {}
int main() {
f(42);
g(42, nullptr);
}
GCC 和 Clang 都对调用 f 感到满意,但对调用 g 却不满意。
在对f 的调用中,虽然T... 出现在非推导上下文中,但最终推导为空。这似乎是由于[temp.arg.explicit]/4:
... 没有以其他方式推导的尾随模板参数包 ([temp.variadic]) 将被推导为模板参数的空序列。 ...
然而,在对g 的调用中,T... 还出现在推断的上下文中这一事实似乎会导致推断尝试并失败导致g 变得不可行。一旦尝试扣除但失败,似乎没有“回退”到T... 为空。
- 这种行为是有意的吗?如果是,为什么?
- 如果是这样,“未以其他方式推断”的措辞是否旨在指定此行为? (即,这意味着只有当包出现在没有推导的上下文中时才会出现空回退)
- 如果是这样,这个措辞是否足够清楚?似乎“未以其他方式推断”的另一种说法是“要么没有进行演绎,要么尝试了演绎但失败了”。
【问题讨论】:
-
我认为这是有意为之,因为我想不出可能的替代阅读方式。
-
完全没有模板:
void f(int*) { } void f(double*) { } void g() { f(nullptr); }- 对我来说似乎是一致的(我们真正想要使用模板产生的许多重载中的哪一个?)。我宁愿把问题转过来:在非推导上下文中允许空模板参数推导的原因是什么? -
@Aconcagua 可能与在非推断上下文中采用默认模板参数的原因相同godbolt.org/z/XjG_D1
-
@Rakete1111 所以空参数列表会被视为“默认”吗?以某种方式看起来很合理......
-
@Brian 看来 nullptr_t 是类型推断系统的死胡同。它无法从 nullptr 转到“指向某种 S 的指针”。作为一种可能的解决方法,您可以提供一个提示: template
S * sp_conv(nullptr_t zp) { return zp; } ;然后调用 g(42, sp_conv(nullptr));这适用于 GCC v8 和 Clang v7。
标签: c++ templates language-lawyer variadic-templates