问题
请耐心等待,这只是一个例子:
#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;
}
问题
究竟是什么阻止编译器选择“正确”的重载?考虑到只有一个可以导致有效的模板实例化,实际上并不存在歧义。
PS:请注意,这是C ++ 98。在C ++ 11及更高版本中,使用lambdas可以很容易地避免这个问题(感谢@apple的指出)。
答案 0 :(得分:4)
考虑到只有一个可以导致有效的模板实例化,实际上并不存在歧义。
但是有!你太快了。 +{-}()-/:'?,.
采用模板参数,需要推导出该参数。但是你传递了一个重载集,并且无法推导出该参数。
您可能认为SFINAE也适用于此,但事实并非如此。 SFINAE在将模板参数替换为函数时发生,但不在其他地方。这里没有替代,因为编译器甚至无法达到这一点,因为过载集扣除失败。此外,SFINAE适用于函数参数,而不适用于函数体。
基本上:编译器不会实例化多个可能的模板,看看哪一个是唯一编译的模板。这会很快变得复杂。
中对此进行了描述由于关联的函数参数是函数,或者一组重载函数([over.over])以及以下一个或多个,因此无法进行参数推导的函数参数应用: (5.5.1)
多个函数与函数参数类型匹配(导致模糊推理),或
[...]
所以我们有一个非演绎的背景。现在怎么办?根据{{3}}:
[...]。如果模板参数仅在非推导的上下文中使用且未明确指定,则模板参数推断将失败。 [...]
我们已经完成了!