bounty将在6天后过期。回答此问题可获得+50声望奖励。Brian Bi希望引起更多人关注此问题。
考虑以下示例(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. varidic])将被导出为一个空的模板参数序列。...
然而,在对g
的调用中,T...
另外 * 出现在推导上下文 * 中,这导致推导被 * 尝试并失败 *,这似乎导致g
变得不可用。一旦推导被尝试并失败,似乎没有“后退”到T...
为空。
- 这种行为是故意的吗?如果是,为什么?
- 如果是,“未以其他方式推导”的措辞是否指定了此行为?(* 即 *,它意味着空回退仅在包未出现在推导上下文中时发生)
- 如果是这样的话,这个措辞是否足够清楚?“没有以其他方式推导”的另一种似乎合理的阅读是“要么没有进行推导,要么尝试推导但失败了”。
1条答案
按热度按时间rseugnpd1#
...一个未被导出的尾随模板参数包([temp. varidic])将被导出为一个空的模板参数序列。...
可以说,* 没有以其他方式推导 * 不是一个应该以某种方式或自动放松一些其他规则的子句,或者实际上它与为什么它是畸形的无关(与我认为你所暗示的相反)。
这个“另一个规则”也许可以通过另一个非常简单的例子得到最好的证明:
上面的代码编译失败,为什么?因为即使
short
无缝地转换为int
(promotion),它也不是同一类型。此外,由于
nullptr
只有std::nullptr_t
的一些 plain 类型-它非常不适合参与模板参数推导 *。所以,让我们暂时忘掉非推导的语境,试试推导的语境:
并且两者都由于相同的原因而失败。
现在,如果您希望允许转换-那么使用身份模板-这甚至可以用于非推导上下文!
对于您的情况,示例可能类似于(c++2a):
这是有效的。(请注意,如果您没有c++20,只需自己编写身份模板)
正如评论中所说,把问题转过来也许会引出一个更有趣的问题?