使用带有转换的模板

Using a template with transform

本文关键字:转换      更新时间:2023-10-16

我在c++中使用算法库的转换函数时遇到问题。我想将它与一元模板函数一起使用,这意味着我的转换函数需要3个迭代器和该函数作为参数。然而,我的程序崩溃了,编译器告诉我缺少一个没有意义的参数,因为我的函数是一元函数,而不是二进制运算。

我的代码如下:

template <typename T>
T reciprocal ( T value )
{ return (T)1/value ; }
int main()
{
    vector<int> vec(5, 2);
    transform(vec.begin(), vec.end(), vec.begin(), reciprocal);
}

是否禁止使用带有转换的模板?

reciprocal是一个函数模板,而std::transform无法确定要使用哪种特殊化。你需要明确并给它一个模板参数:

transform(vec.begin(), vec.end(), vec.begin(), reciprocal<int>);

请注意,在您的情况下,您将执行整数除法1/2,这将给您一堆零。为了说明浮点倒数计算,以及reciprocal的模板参数不必与vec的模板参数相同,您可以尝试以下操作:

std::vector<double> vec2;
std::transform(vec.begin(), 
               vec.end(), 
               std::back_inserter(vec2), 
               reciprocal<double>);

这将导致vec2包含五个0.5值。

但是我的程序崩溃了,编译器告诉我缺少一个没有意义的参数,因为我的函数是一元函数,而不是二进制运算

您应该清楚地说明您的程序是否编译然后崩溃,而不是编译,编译器崩溃以及缺少什么类型的参数。

现在猜测一下你的意思,问题是transform模板没有对最后一个参数施加任何限制,因此编译器无法确定reciprocal的哪些重载(专门化)应该传递给转换模板。您可以手动指定您想要的:

transform(vec.begin(), vec.end(), vec.begin(), &reciprocal<int>);

或者可以禁止使用带有转换的模板?

这本身就是一个有趣的点,因为它显示了一些常见的误解。template是创建其他元素的蓝图。在这种情况下,您有一个函数模板,它只是一个蓝图,编译器将通过替换模板参数来生成不同的功能。在需要函数的任何上下文中,都不能使用功能模板,尽管可以使用specialized(即,从function template生成的function)。

在某些情况下,您可以使用模板的名称来引用具体的专业化,这会变得有点令人困惑。也就是说,在类模板定义(或其成员的定义)中,模板的名称可以用来引用专业化。在函数模板的情况下,模板的名称可用于指代在编译器将能够丢弃除一个专门化之外的所有专门化的情况下功能模板

int call(int (*ptr)(int)) { return ptr(5); }
call(reciprocal);       // [1]

在[1]中,reciprocal指的是函数模板的所有

可能的特殊化,但这种特殊使用是允许的,因为这些特殊化中只有一个(即reciprocal<int>)可以用作call的参数。

但这些都是例外,主要的一点是类模板函数模板别或者功能,而是编译器可以从中创建类和函数的生成器。

现在,您需要依靠编译器来推断实例化reciprocal的正确类型,但由于您尚未指定参数类型,因此无法执行此操作。

正如@jaunchopanza已经指出的那样,您可以通过指定正确的类型来解决这个问题。通过稍微不同地构建代码:

struct reciprocal {
    template <typename T>
    T operator()(T value)
    { 
        return (T) 1 / value; 
    }
};

你可以让编译器来推导类型,所以代码如下:

std::transform(vec.begin(), vec.end(), vec.begin(), reciprocal());

工作正常(不过请注意parens以创建reciprocal的实例)。

当然,ints上的倒数通常没有多大意义——它所能产生的只是0或1(或0输入的未定义行为)。您可能想要使用浮点类型来获得有意义的结果:

std::vector<double> vec(5, 2);
std::transform(vec.begin(), vec.end(), vec.begin(), reciprocal());