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