将函数指针传递给算法时选择正确的重载
Pick the right overload when passing function pointer to algorithm
问题
请耐心等待,这真的只是一个例子:
#include <algorithm>
#include <iterator>
struct foo {
static int my_transform(int x) { return x;}
static std::vector<int> my_transform(std::vector<int> x){
std::vector<int> result;
std::transform(x.begin(),x.end(),std::back_inserter(result),my_transform);
return result;
}
};
我期望发生的事情
my_transform
有两种可能的重载,但只有一种会导致格式正确的模板实例化,而另一种会导致模板实例化格式错误。我希望格式错误的被丢弃,上面的编译。
到底发生了什么
main.cpp:165:75: error: no matching function for call to
‘transform(std::vector<int>::iterator, std::vector<int>::iterator,
std::back_insert_iterator<std::vector<int> >, <unresolved overloaded function type>)’
std::transform(x.begin(),x.end(),std::back_inserter(result),my_transform);
^
如何修复
将函数指针转换为正确的类型解决了歧义并编译:
static std::vector<int> foo::my_transform(std::vector<int> x){
std::vector<int> result;
typedef int (*my_transform_t)(int);
std::transform(x.begin(),
x.end(),
std::back_inserter(result),
static_cast<my_transform_t>(my_transform));
return result;
}
问题
究竟是什么阻止了编译器选择 "correct" 重载?考虑到只有一个可以导致有效的模板实例化,所以真的没有歧义。
PS:注意这是C++98。在 C++11 及更高版本中,使用 lambda 可以轻松避免该问题(感谢@appleapple 指出)。
Considering that only one can result in a valid template instantiation there isn't really a ambiguity.
但是有!你只是太快了。 std::transform
采用模板参数,需要推导该参数。但是您向它传递了一个重载集,并且无法推导该参数。
你可能认为SFINAE也适用于此,但事实并非如此。 SFINAE 在将模板参数替换为函数时发生,但在其他任何地方都不会发生。这里没有替换,因为重载集推演失败,编译器连那个点都达不到。此外,SFINAE 适用于函数参数,而不适用于函数体。
基本上:编译器不会实例化多个可能的模板并查看哪一个是唯一编译的模板。那会很快变得复杂。
这在[temp.deduct.type]p5中有描述:
A function parameter for which argument deduction cannot be done because the associated function argument is a function, or a set of overloaded functions ([over.over]), and one or more of the following apply:
(5.5.1)
more than one function matches the function parameter type (resulting in an ambiguous deduction), or
[...]
所以我们有一个非推导的上下文。现在怎么办?根据[temp.deduct]p4:
[...]. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails. [...]
大功告成!
问题
请耐心等待,这真的只是一个例子:
#include <algorithm>
#include <iterator>
struct foo {
static int my_transform(int x) { return x;}
static std::vector<int> my_transform(std::vector<int> x){
std::vector<int> result;
std::transform(x.begin(),x.end(),std::back_inserter(result),my_transform);
return result;
}
};
我期望发生的事情
my_transform
有两种可能的重载,但只有一种会导致格式正确的模板实例化,而另一种会导致模板实例化格式错误。我希望格式错误的被丢弃,上面的编译。
到底发生了什么
main.cpp:165:75: error: no matching function for call to
‘transform(std::vector<int>::iterator, std::vector<int>::iterator,
std::back_insert_iterator<std::vector<int> >, <unresolved overloaded function type>)’
std::transform(x.begin(),x.end(),std::back_inserter(result),my_transform);
^
如何修复
将函数指针转换为正确的类型解决了歧义并编译:
static std::vector<int> foo::my_transform(std::vector<int> x){
std::vector<int> result;
typedef int (*my_transform_t)(int);
std::transform(x.begin(),
x.end(),
std::back_inserter(result),
static_cast<my_transform_t>(my_transform));
return result;
}
问题
究竟是什么阻止了编译器选择 "correct" 重载?考虑到只有一个可以导致有效的模板实例化,所以真的没有歧义。
PS:注意这是C++98。在 C++11 及更高版本中,使用 lambda 可以轻松避免该问题(感谢@appleapple 指出)。
Considering that only one can result in a valid template instantiation there isn't really a ambiguity.
但是有!你只是太快了。 std::transform
采用模板参数,需要推导该参数。但是您向它传递了一个重载集,并且无法推导该参数。
你可能认为SFINAE也适用于此,但事实并非如此。 SFINAE 在将模板参数替换为函数时发生,但在其他任何地方都不会发生。这里没有替换,因为重载集推演失败,编译器连那个点都达不到。此外,SFINAE 适用于函数参数,而不适用于函数体。
基本上:编译器不会实例化多个可能的模板并查看哪一个是唯一编译的模板。那会很快变得复杂。
这在[temp.deduct.type]p5中有描述:
A function parameter for which argument deduction cannot be done because the associated function argument is a function, or a set of overloaded functions ([over.over]), and one or more of the following apply: (5.5.1)
more than one function matches the function parameter type (resulting in an ambiguous deduction), or
[...]
所以我们有一个非推导的上下文。现在怎么办?根据[temp.deduct]p4:
[...]. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails. [...]
大功告成!