在您的代码中, 有两种不同的用法auto
,一种在参数中,另一种在返回类型中。在参数的情况下,每次使用都会auto
引入一个唯一的模板类型参数,所以正如 Jerry 提到的那样,它相当于:
// 1
template <typename A, typename B>
auto add(A a, B b) {
return a + b;
}
在这种情况下,如果您对不同类型的参数有任何约束(必须相同,可以是一个,但会有其他),那么显式模板语法提供了更好的选择。如果您想在参数上使用 SFINAE(通过额外的模板参数),这是特别的:
// 2
template <typename A, typename B,
typename _1 = typename A::iterator, // A has nested iterator type
typename _2 = typename B::iterator> // So does B
auto f(A a, B b);
请注意,我有意避免在返回类型上使用 SFINAE,因为它会以某种方式干扰您对 的其他使用auto
,但这可能是另一种选择:
// 3
auto f(auto a, auto b)
-> typename enable_if<has_nested_iterator<decltype(a)>::value
&& has_nested_iterator<decltype(b)>::value,
[return type] >::type;
但是正如您所看到的,它变得更加复杂,因为您需要使用尾随返回类型并通过decltype
.
您的示例中的第二种用法与auto
此完全不同,它在返回类型中具有推导的返回类型。推导的返回类型是 C++11 中已经为 lambda 提供的功能,但已推广到所有函数模板。该功能允许编译器通过检查return
主体内的不同语句来查找函数返回的类型。优点是,如果您的模板只有一个返回表达式,您可以避免输入该表达式两次,一次用于返回类型,另一次用于实际代码:
// 4
auto add(auto a, auto b) -> decltype(a + b) { // 'a + b' here
return a + b; // 'a + b' also here
}
缺点是编译器需要检查函数体以确定将返回的类型,这必然是类型替换后的。因此,具有推导类型的函数的 return 语句不能在 SFINAE 表达式中使用,这可能会使函数用户的生活变得复杂:
// 5
auto doAdd(auto a, auto b)
-> typename enable_if<is_integral<decltype(add(a,b))>>::type
{
return add(a,b);
}
SFINAE不会doAdd
从重载解析集中移除上述重载,如果您将其称为doAdd(1, 1.)
.