gpt4 book ai didi

c++ - 函数模板参数推导模板参数vs默认模板参数vs返回类型

转载 作者:行者123 更新时间:2023-12-05 01:08:14 25 4
gpt4 key购买 nike

这是一个关于模板参数用作模板参数与用作默认模板参数与返回类型时模板推导如何工作的问题。

1:普通模板参数

经测试,使用 GCC 和 VS 编译以下代码段无法推断出由 std::enable_if_t

定义的模板参数
#include <iostream>
#include <type_traits>

template< class T, std::enable_if_t< std::is_integral_v< T > > >
void SwapInPlace( T& left, T& right )
{
left = left ^ right;
right = left ^ right;
left = left ^ right;
}

template< class T, std::enable_if_t< std::is_floating_point_v< T > > >
void SwapInPlace( T& left, T& right )
{
left = left - right;
right = left + right;
left = right - left;
}

int main()
{
int i1 = 10;
int i2 = -120;
std::cout << i1 << " " << i2 << std::endl;
SwapInPlace( i1, i2 );
std::cout << i1 << " " << i2 << std::endl;

double d1 = 1.1234;
double d2 = 2.5678;
std::cout << d1 << " " << d2 << std::endl;
SwapInPlace( d1 , d2 );
std::cout << d1 << " " << d2 << std::endl;

return 0;
}

VS: error C2783: 'void SwapInPlace(T &,T &)': could not deducetemplate argument for '__formal'

GCC: couldn't deduce template parameter -anonymous-

2:默认模板参数

将第二个模板参数声明为默认模板参数使得推导工作正常:

template< class T, class = std::enable_if_t< std::is_integral_v< T > > >
void SwapInPlace( T& left, T& right )
{...}

template< class T, class = std::enable_if_t< std::is_floating_point_v< T > >, bool = true >
void SwapInPlace( T& left, T& right )
{...}

在第二次重载中添加了 bool = true 以避免编译错误,因为无法根据默认模板参数重载函数模板。只想专注于扣除在这里可以正常工作的事实。如果只有一个模板使用默认参数,例如对于 std::is_integral,只要我们将正确的参数传递给它,它就会编译并正常工作。

3:返回类型

在返回类型的情况下,一切都可以编译并且运行良好:

template< class T >
std::enable_if_t < std::is_integral_v< T > >
SwapInPlace( T& left, T& right )
{...}

template< class T >
std::enable_if_t < std::is_floating_point_v< T > >
SwapInPlace( T& left, T& right )
{...}

4:带默认值的模板参数

另一种编译此代码的方法是为 std::enable_if 定义的模板参数添加默认值。在最后一个右尖括号之前添加了 * = nullptr,所以如果 std::enable_if 条件评估为 true 那么我们的第二个参数变为 void * 默认值 nullptr:

template< class T, std::enable_if_t< std::is_integral_v< T > >* = nullptr >
void SwapInPlace( T& left, T& right )
{...}

template< class T, std::enable_if_t< std::is_floating_point_v< T > >* = nullptr >
void SwapInPlace( T& left, T& right )
{...}

所以问题是:在这 4 种情况下如何演绎:为什么它在第一种情况下失败而在其他三种情况下成功?

最佳答案

how deduction works in this 4 cases: why it fails in first case and succeds in three other?


第一种情况

template< class T, std::enable_if_t< std::is_integral_v< T > > >
void SwapInPlace( T& left, T& right )

假设 std::is_integral_v 为真;使用 std::enable_if_t 你得到的替换

template< class T, void>
void SwapInPlace( T& left, T& right )

这不是一个有效的 C++ 代码,因为它请求了一个 void 值(用于第二个模板参数),但 void 不能有一个有效值。

假设您用 int(一种接受有效值的类型)替换第二个模板参数的类型

// .........................................................VVVVVV
template< class T, std::enable_if_t< std::is_integral_v< T >, int > >
void SwapInPlace( T& left, T& right )

如果你得到整数 T

template <class T, int>
void SwapInPlace( T& left, T& right )

这是有效的,但是......假设调用是

int a{1}, b{2};

SwapInPlace(a, b); // compilation error

你有 Tab 推导出为 int 但编译器无法决定第二个(未命名的)模板参数的值。

因此,要进行有效调用,您必须显式调用函数的第二个模板参数

SwapInPlace<int, 0>(a, b); // OK

这样调用是有效的,因为没有进行推导,并且明确地说 Tint 并且 int 参数是 0 .

这行得通,但不舒服,因为您必须明确可以推断出的 T

为避免此问题,您可以为第二个模板参数添加默认值

// .........................................................VVVVVV.VVVV
template< class T, std::enable_if_t< std::is_integral_v< T >, int > = 0 >
void SwapInPlace( T& left, T& right )

所以,如果是 T 积分,你会得到

// ....................VVVV
template <class T, int = 0>
void SwapInPlace( T& left, T& right )

现在是简单的调用

int a{1}, b{2};

SwapInPlace(a, b); // OK now

因为 T 被推导出为 int 并且第二个模板参数默认为零。

有效,但您可以观察到您现在处于第四种情况(使用 int 而不是 void *0 而不是nullptr)


第二种情况

第二种情况是个坏主意。

template< class T, class = std::enable_if_t< std::is_integral_v< T > > >
void SwapInPlace( T& left, T& right )
{...}

T为整数时

template< class T, class = void>
void SwapInPlace( T& left, T& right )
{...}

这是一个有效的代码,其中 T 可以从 leftright 参数推导出来,而第二个未命名的参数是默认,所以不需要显式。

但是当 T 不是整数时,SFINAE 失败只会丢弃第二个模板参数的默认值,所以你得到

template< class T, class>
void SwapInPlace( T& left, T& right )
{...}

并且代码是有效的,但是第二个模板参数不是默认的,所以必须是显式的。

所以,几乎和第一种情况一样,你有

float a{1.0f}, b{2.0f};

SwapInPlace(a, b); // compilation error
SwapInPlace<float, void>(a, b); // compile

不好的部分是默认值不能区分函数签名;所以如果你有两个 SwapInPlace() 替代函数

template< class T, class = std::enable_if_t< std::is_integral_v< T > > >
void SwapInPlace( T& left, T& right )
{...}

template< class T, class = std::enable_if_t< not std::is_integral_v< T > > >
void SwapInPlace( T& left, T& right )
{...}

在你有替换之后

template< class T, class = void>
void SwapInPlace( T& left, T& right )
{...}

template< class T, class>
void SwapInPlace( T& left, T& right )
{...}

因此,您有两个具有完全相同签名的函数(请记住:= void 不算数)。这对于 C++ 规则是 Not Acceptable ,因此会出现编译错误。

建议:避免第二种方式,因为当你必须开发替代功能时不起作用。


第三种情况

template< class T >
std::enable_if_t < std::is_integral_v< T > > SwapInPlace( T& left, T& right )
{...}

据我所知,这是最简单的情况。

如果 T 是整数,则得到

template< class T >
void SwapInPlace( T& left, T& right )

这是有效的代码,所以该功能被启用。

如果 T 不是整数,则丢失返回值

template< class T >
SwapInPlace( T& left, T& right )

所以代码无效,所以功能被禁用。


第四种情况:见第一种情况

关于c++ - 函数模板参数推导模板参数vs默认模板参数vs返回类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66290487/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com