gpt4 book ai didi

c++ - 传递重载函数指针及其参数时的错误类型推导

转载 作者:行者123 更新时间:2023-12-01 12:33:59 27 4
gpt4 key购买 nike

我正在尝试为 std::invoke 提供一个包装器即使在函数重载时也要做推断函数类型的工作。
(我昨天向 related question 询问了可变参数和方法指针版本)。

当函数有一个参数时,此代码 (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));
}

对于更多的输入参数,显然需要减少代码膨胀,但这是一个单独的问题。

如果用户的重载仅因 const/references 不同,如下所示:
#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 &)

(int &&)
(const int &)



和铿锵++:

(int &)
(const int &)

(int &&)
(int &&)



(感谢 geza 指出 clang 的输出不同)。

所以 Invoke有未定义的行为。

我怀疑元编程将是解决这个问题的方法。无论如何,是否可以在 Invoke 处正确处理类型推导?地点?

最佳答案

理论

对于 每个 函数模板Invoke ,模板参数推导(必须成功才能考虑重载决议)考虑 每个 Foo看看它是否可以推导出 的许多模板参数(这里是两个)一个 涉及函数参数(func)。只有一个Foo,才能整体推演成功。匹配(因为否则无法推断 S )。 (评论中或多或少地说明了这一点。)

第一个(“按值(value)”)Invoke永不生存:它可以从 Foo 中的任何一个推断出来s。同样,第二个(“非 const 引用”)重载接受前两个 Foo s。请注意,这些适用 不管 Invoke 的另一个论点(对于 arg )!

第三个( const T& )重载选择对应的 Foo重载和推导T = int ; last 对最后一个重载做同样的事情(其中 T&& 是一个正常的右值引用),因此尽管它的 拒绝左值参数通用引用(在这种情况下将 T 推导出为 int& (或 const int& ),并与 func 的推导相冲突)。

编译器

如果 arg 的参数是一个右值(而且,像往常一样,不是 const),两者都是合理的 Invoke重载成功扣除,T&&重载应该获胜(因为它将右值引用绑定(bind)到 右值 )。

对于评论中的案例:

template <typename U>
void Bar (U &&);
int main() {
int num;
Invoke<void>(&Bar, num);
}
&Bar 中不进行任何扣除由于涉及到函数模板,所以 T在每种情况下都成功推断(如 int )。然后,扣除发生 再次为每个案例识别 Bar要使用的特化(如果有),推导出 U失败, int& , const int& , 和 int&分别。 int&案例是相同的,显然更好,所以这个电话是模棱两可的。

所以 铿锵 就在这里。 (但这里没有“未定义的行为”。)

解决方案

我没有给你一个普遍的答案;由于某些参数类型可以接受多个值类别/常量限定对,因此在所有这些情况下正确模拟重载决策并不容易。有人提出以一种或另一种方式具体化重载集。您可能会考虑这些方面的当前技术之一(如 通用 lambda 每个目标函数名称)。

关于c++ - 传递重载函数指针及其参数时的错误类型推导,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60585022/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com