C++ Isn';std::function的模板参数(签名)是其类型的一部分吗?

C++ Isn';std::function的模板参数(签名)是其类型的一部分吗?,c++,c++11,std-function,C++,C++11,Std Function,给定以下代码,歧义背后的原因是什么?我可以绕过它吗?还是我必须保留(恼人的)显式强制转换 #include <functional> using namespace std; int a(const function<int ()>& f) { return f(); } int a(const function<int (int)>& f) { return f(0); } int x() { return 22; }

给定以下代码,歧义背后的原因是什么?我可以绕过它吗?还是我必须保留(恼人的)显式强制转换

#include <functional>

using namespace std;

int a(const function<int ()>& f)
{
    return f();
}

int a(const function<int (int)>& f)
{
    return f(0);
}

int x() { return 22; }

int y(int) { return 44; }

int main()
{
    a(x);  // Call is ambiguous.
    a(y);  // Call is ambiguous.

    a((function<int ()>)x);    // Works.
    a((function<int (int)>)y); // Works.

    return 0;
}
#包括
使用名称空间std;
int a(常数函数和f)
{
返回f();
}
int a(常数函数和f)
{
返回f(0);
}
int x(){return 22;}
int y(int){return 44;}
int main()
{
a(x);//调用不明确。
a(y);//调用不明确。
a((函数)x);//起作用。
a((函数)y);//起作用。
返回0;
}
有趣的是,如果我用
function
参数注释掉
a()
函数,并在main中调用
a(x)
,编译将正确失败,因为
x
与唯一可用的
a()
函数的参数
function
之间的类型不匹配。如果编译器在这种情况下失败,那么当存在两个
a()
函数时,为什么会出现歧义


我试过VS2010和g++v。4.5. 两者都给了我完全相同的歧义。

问题是
函数和
函数都可以从同一个函数构造。这是VS2010中
std::function
的构造函数声明的样子:

template<class _Fx>
function(_Fx _Func, typename _Not_integral<!_Is_integral<_Fx>::value, int>::_Type = 0);
现在,当编译器搜索重载集的有效函数时,如果不存在完美拟合函数,它会尝试转换参数。转换可以通过函数参数的构造函数进行,也可以通过函数参数的转换运算符进行。在我们的例子中,是前者。
编译器尝试
a
的第一个重载。为了使其可行,它需要进行转换。要将
int(*)(
转换为
myfunc
,它将尝试
myfunc
的构造函数。作为一个可以接受任何东西的模板,转换自然会成功。
现在,它对第二个重载进行相同的尝试。构造器仍然是相同的,并且仍然接受任何给定的内容,转换也可以工作。
在重载集中只剩下2个函数,编译器是一只悲伤的熊猫,不知道该做什么,所以它只是说调用是模糊的


因此,最后,模板的
签名
部分在声明/定义时确实属于该类型,但在构建对象时不属于该类型


编辑
当我全神贯注地回答标题问题时,我完全忘记了你的第二个问题(

我可以绕过它吗?还是我必须保留(恼人的)显式强制转换

#include <functional>

using namespace std;

int a(const function<int ()>& f)
{
    return f();
}

int a(const function<int (int)>& f)
{
    return f(0);
}

int x() { return 22; }

int y(int) { return 44; }

int main()
{
    a(x);  // Call is ambiguous.
    a(y);  // Call is ambiguous.

    a((function<int ()>)x);    // Works.
    a((function<int (int)>)y); // Works.

    return 0;
}
好吧,你有三个选择

  • 保留演员阵容
  • 制作一个适当类型的
    函数
    对象并传递

    函数fx=x;
    函数fy=y;
    a(外汇);
    a(fy);

  • 在函数中隐藏繁琐的强制转换,并使用TMP获得正确的签名

TMP(template metaprogramming,模板元编程)版本非常冗长,带有样板代码,但它对客户端隐藏了转换。可以找到一个示例版本,它依赖于部分专用于函数指针类型的
get_signature
metafunction(并提供了一个很好的示例,说明模式匹配如何在C++中工作):

模板
结构获取_签名;
模板
结构获取\u签名{
typedef R type();
};
模板
结构获取\u签名{
类型(A1);
};
当然,这需要根据您想要支持的参数数量进行扩展,但这只需扩展一次,然后将其嵌入
的“get_signature.h”
标题中:

另一个我认为是立即丢弃的选项是SFIAE,它将引入比TMP版本更多的样板代码。


所以,是的,这是我知道的选择。希望其中一个对你有用。:)

std::function
有一个采用任意类型(即除
T
以外的类型)的转换向量。当然,在这种情况下,该ctor将导致类型不匹配错误,但编译器并没有做到这一点——调用是不明确的,因为ctor存在。

我已经多次看到这个问题出现。现在编译此代码时没有歧义(作为一致性扩展)

过期更新

这个“扩展”非常流行,在C++14中实现了标准化(尽管我个人并不负责完成这项工作)

事后看来,我并没有完全正确地得到这个扩展。本月早些时候(2015-05-09)委员会投票有效地改变了可调用的定义,以便如果<代码> STD::函数< /COD>有<代码>空隙返回类型,它将忽略已包装函子的返回类型,但如果其他情况匹配,则仍认为它是可调用的。而不是认为它不可调用


此C++14后调整不会影响此特定示例,因为所涉及的返回类型始终是
int

,下面是一个如何在检查构造函数参数可调用性的类中包装
std::function
的示例:

template<typename> struct check_function;
template<typename R, typename... Args>
struct check_function<R(Args...)>: public std::function<R(Args...)> {
    template<typename T,
        class = typename std::enable_if<
            std::is_same<R, void>::value
            || std::is_convertible<
                decltype(std::declval<T>()(std::declval<Args>()...)),
                R>::value>::type>
        check_function(T &&t): std::function<R(Args...)>(std::forward<T>(t)) { }
};
模板结构检查功能;
模板
结构检查函数:公共标准::函数{
模板::值>::类型>
check_函数(T&&T):std::function(std::forward(T)){
};
这样使用:

int a(check_function<int ()> f) { return f(); }
int a(check_function<int (int)> f) { return f(0); }

int x() { return 22; }
int y(int) { return 44; }

int main() {
    a(x);
    a(y);
}
inta(检查函数f){return f();}
int a(check_函数f){返回f(0);}
int x(){return 22;}
int y(int){return 44;}
int main(){
a(x);
a(y);
}
请注意,这与函数签名重载不同,因为它将可转换参数(和返回)类型视为等效类型。对于精确重载,这应该可以工作:

template<typename> struct check_function_exact;
template<typename R, typename... Args>
struct check_function_exact<R(Args...)>: public std::function<R(Args...)> {
    template<typename T,
        class = typename std::enable_if<
            std::is_convertible<T, R(*)(Args...)>::value>::type>
        check_function_exact(T &&t): std::function<R(Args...)>(std::forward<T>(t)) { }
};
模板结构检查功能精确;
模板
结构检查函数精确:公共标准::函数{
模板::类型>
检查函数精确(T&&T):标准::函数(标准::前进(T)){
template<typename> struct check_function_exact;
template<typename R, typename... Args>
struct check_function_exact<R(Args...)>: public std::function<R(Args...)> {
    template<typename T,
        class = typename std::enable_if<
            std::is_convertible<T, R(*)(Args...)>::value>::type>
        check_function_exact(T &&t): std::function<R(Args...)>(std::forward<T>(t)) { }
};