Some Notes about C++
non-deduced context
Definition
为了方便理解,此处仅讨论如下形式的函数模板:
template <typename T> void foo(P t)
(此处P
为可能包含T
的类型表达式) 即仅有一个模板参数且为类型参数的单参数函数。
在我们进行模板参数推导的时候,我们实质上是在做一个类似解方程的过程。对于一个函数调用 foo(a)
, 它给出了一个类型间的相等关系,即 a
的类型(设为 U
)与 P
相同: U = P
。要推导出模板参数的类型,就需要对这一方程进行求解。当这一方程可以被解出时,它就被称为一个 deducible context;当这一方程无法被解出时,它就是一个 non-deduced context。用一个例子来说明:
1 | template <typename T> |
在这个例子中,对 foo(std::vector<int>{1})
进行模板参数推导的时候,我们可以得到方程 std::vector<T> = std::vector<int> (a = {1})
,由此解出 T = int
完成推导,这是一个 deducible context;对 bar(1)
进行模板参数推导的时候,我们可以得到方程 std::type_identity<T>::type = int (b = 1)
,这是一个无法求解的方程(因为 type_identity
可能有无穷多种特化满足这一方程,无法一一枚举求解),于是会产生编译错误,所以这是一个 non-deduced context。(事实上,在所有的 non-deduced context 中,最常见的一种就是模板参数 T
出现在 ::
的左边。这种方程无法求解的原因可参见上文。)
Application
知道了 non-deduced context 的概念以后,我们就可以用它来解决一些问题(或者 play tricks):
1 | template <typename T> |
在上述代码中,对 foo(4.2, 1)
进行模板参数推导的时候,我们可以得到两个方程: T = double (a = 4.2)
, T = int (b = 1)
。这两个方程分别给出了两个不同的解,它们的地位是平等的,所以发生了冲突:编译器不知道应该选择哪一个解,导致编译错误。但在对 bar(4.2, 1)
进行模板参数推导的时候就不会出现这个问题,因为 std::type_identity_t<T> b
是一个 non-deduced context,不会给出任何解。此时对于 T
编译器只会得到 T = double
的唯一解,并不会像 foo
一样产生冲突,从而解决了编译错误。
Reference
What is a nondeduced context? - SoF type_identity
- cppreference Template Argument Deduction - cppreference