为什么我不能将在函数中定义的函数对象传递给另一个函数?

5
我发现函数对象可以用来模拟在一个函数内部定义另一个函数,就像这样:
using namespace std;
int main(int argc, char* argv[])
{
    struct MYINC {
        int operator()(int a) { return a+1; }
    } myinc;
    vector<int> vec;
    for (int i = 0; i < 10; i++) vec.push_back(myinc(i));
    return 0;
}

但如果我将它传递给一个外部函数(例如下面的std::transform),那么就会出现编译错误,提示:error: no matching function for call to ‘transform(std::vector<int>::iterator, std::vector<int>::iterator, std::vector<int>::iterator, main(int, char**)::MYINC&)’

using namespace std;
int main(int argc, char* argv[])
{
    struct MYINC{
        int operator()(int a) { return a+1; }
    } myinc;
    vector<int> vec;
    for (int i = 0; i < 10; i++) vec.push_back(i);
    transform(vec.begin(), vec.end(), vec.begin(), myinc);
    return 0;
}

所以我把这个定义放在了主函数之外,现在一切都正常了。

using namespace std;
struct MYINC{
    int operator()(int a) { return a+1; }
} myinc;
int main(int argc, char* argv[])
{
    vector<int> vec;
    for (int i = 0; i < 10; i++) vec.push_back(i);
    transform(vec.begin(), vec.end(), vec.begin(), myinc);
    return 0;
}

我发现了一个重复的答案 - Yantao Xie
2个回答

6

两个版本都可以在C++11编译器g++4.8.2中正常编译。

然而,在C++03中,使用本地类型实例化模板会导致编译器发生错误,因为C++03不支持这种操作。

如果出现这种问题,一种解决方案是使用更高版本的编译器或其他编译器。

另一种解决方案是利用即使在C++03中,您也可以通过将其作为局部类的静态成员函数来定义“真正”的函数(在C++11中,您还可以通过使用lambda表达式来实现)。

但是,除了解决此类问题外,函数对象相对于“真正”的函数具有普遍的性能优势,即使用类的对象而不仅仅是函数指针,并且将相关的operator()设为inline后,编译器可以更好地进行优化,因为它知道函数的实现。


很棒且实用的答案!知道在C++11标准中哪里定义了支持这种新功能会很好。 - Dan Nissenbaum
2
在C++98中(在C++03中没有更正),在§14.3.1/2中规定:“本地类型、无链接的类型、未命名的类型或由这些类型组成的类型不得用作模板类型参数”。这个要求在C++11中被删除了。因此,与其在C++11中“定义为支持”,不如将能力隐含地指定为没有限制要求,这很难引用。 :-) - Cheers and hth. - Alf

1
由于某些原因,对于可以用来实例化模板的类型存在着一个著名且非常恼人的限制,在C++ 03中无法使用局部定义的类作为模板参数,原因就是这样。顺便说一下,这使得使用<algorithm>库变得相当困难,因为您无法将代码的上下文保持在本地,而被迫将所有函数对象、比较器等放置在命名空间级别,并为它们编写有趣的名称并将它们放置在使用点之远处。

关于“本地定义的类不能与模板一起作为参数使用”的问题,这在C++03中是如此,但在当前标准C++11中已不再存在。关于“仅仅因为”,它与局部类型没有联系。但是,我对为什么那个在C++03中是一个停滞点的细节并不清楚,就像你一样。 - Cheers and hth. - Alf
@Cheersandhth.-Alf:已修复。很高兴知道C++11移除了那个看似无意义的限制。 - 6502

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接