![](/img/trans.png)
[英]Dealing with <unresolved overloaded function type> when passing function pointer to function
[英]Bad type deduction when passing overloaded function pointer and its arguments
我正在尝试为std::invoke
提供一个包装器,以完成推导函数类型的工作,即使函数已重载。
(我昨天为可变参数和方法指针版本问了一个相关问题)。
当函数有一个参数时,此代码 (C++17) 在正常重载条件下按预期工作:
#include <functional>
template <typename ReturnType, typename ... Args>
using FunctionType = ReturnType (*)(Args...);
template <typename S, typename T>
auto Invoke (FunctionType<S, T> func, T arg)
{
return std::invoke(func, arg);
}
template <typename S, typename T>
auto Invoke (FunctionType<S, T&> func, T & arg)
{
return std::invoke(func, arg);
}
template <typename S, typename T>
auto Invoke (FunctionType<S, const T&> func, const T & arg)
{
return std::invoke(func, arg);
}
template <typename S, typename T>
auto Invoke (FunctionType<S, T&&> func, T && arg)
{
return std::invoke(func, std::move(arg));
}
更多的输入参数显然需要减少代码膨胀,但这是一个单独的问题。
如果用户的重载仅因常量/引用而不同,如下所示:
#include <iostream>
void Foo (int &)
{
std::cout << "(int &)" << std::endl;
}
void Foo (const int &)
{
std::cout << "(const int &)" << std::endl;
}
void Foo (int &&)
{
std::cout << "(int &&)" << std::endl;
}
int main()
{
int num;
Foo(num);
Invoke(&Foo, num);
std::cout << std::endl;
Foo(0);
Invoke(&Foo, 0);
}
然后Invoke
错误地推断出该函数,输出为 g++:
(int &)
(const int &)(整数&&)
(const int &)
和叮当++:
(int &)
(const int &)(整数&&)
(整数&&)
(感谢 geza 指出 clang 的输出不同)。
因此Invoke
具有未定义的行为。
我怀疑元编程将是解决这个问题的方法。 无论如何,是否可以在Invoke
站点正确处理类型推导?
对于每个函数模板Invoke
,模板参数推导(必须成功才能考虑重载决议)考虑每个Foo
以查看它是否可以为所涉及的一个函数参数( func
)推导出多个模板参数(此处为两个)。 只有当恰好有一个Foo
匹配时,整体推导才能成功(因为否则无法推导S
)。 (这或多或少在评论中有所说明。)
第一个(“按值”) Invoke
永远不会存活:它可以从任何Foo
推导出来。 类似地,第二(“非const
参考”)过载接受前两个Foo
秒。 请注意,无论Invoke
的其他参数如何(对于arg
),这些都适用!
第三个( const T&
)重载选择相应的Foo
重载并推导出T
= int
; 最后一个与最后一个重载(其中T&&
是正常的右值引用)做同样的事情,因此拒绝左值参数,尽管它的通用引用(在这种情况下将T
推导出为int&
(或const int&
)并与func
的推导冲突)。
如果arg
是一个右值(并且像往常一样,不是 const),那么两个合理的Invoke
重载在推导时都会成功,并且T&&
重载应该获胜(因为它将右值引用绑定到一个rvalue )。
对于评论中的案例:
template <typename U>
void Bar (U &&);
int main() {
int num;
Invoke<void>(&Bar, num);
}
由于涉及函数模板,因此不会从&Bar
推导,因此在每种情况下都成功推导了T
(作为int
)。 然后,对每种情况再次进行推导,以确定要使用的Bar
(如果有),分别推导出U
为fail 、 int&
、 const int&
和int&
。 int&
情况是相同的,而且明显更好,所以调用是模棱两可的。
所以Clang就在这里。 (但这里没有“未定义的行为”。)
我没有给你一个通用的答案; 由于某些参数类型可以接受多个值类别/常量限定对,因此在所有这些情况下都不容易正确模拟重载解析。 已经有人提议以某种方式具体化重载集; 您可能会考虑这些方面的当前技术之一(例如每个目标函数名称的通用 lambda )。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.