区分接受常量参数的函数引用/指针和与函数参数同名的非常量参数

Distinguish between function references/pointers accepting const and non-const argument with same name as function parameter

本文关键字:参数 常量 函数 非常 指针 引用      更新时间:2023-10-16

让我们考虑两个同名的函数:

int func(int&)
{
return 7;
}
int func(const int&)
{
return 5;
}

让我们在某处定义int mutableValue = 5call(mutableValue, func)中的模板函数call是否有可能只需要int func(int&)? 我不想使用static_cast<int(&)(int&)>- 因为它太吵了。

朴素实现:

template<typename Arg, typename Ret>
void call(Arg& val, Ret (&fun)(Arg&))
{
fun(val);
}

适用于 GCC,但不适用于 Clang - 解决方案必须适用于两个编译器。

只接受const Arg&很容易。删除const Arg&重载无济于事。

我相信clang是唯一能做到这一点的编译器。这不应该编译。只要要fun的参数是不包含任何函数模板的重载集,就会尝试对重载集的每个成员进行模板参数推断。如果模板参数推导成功用于多个重载,则函数参数将成为非推导上下文 [temp.deduct.call]/6.2。

在所讨论的例子中,要fun的参数是重载集func,它确实不包含任何函数模板。因此,fun的参数推导尝试用于func的两个重载,结果成功。结果,参数fun成为非推导上下文,这意味着无法推断出Ret的参数,并且调用失败,因为没有候选项(这正是 clang 抱怨的(。

若要消除此调用的歧义,只需显式指定第一个模板参数的参数:

call<int>(mutableValue, func)

由于似乎不可能解决一个模板参数推导中的歧义:

如果您同意在呼叫站点更改语法,则可以将呼叫分为两个呼叫/扣除传递:

template<typename Arg>
auto call(Arg& val)
{
return [&](auto (&fun)(Arg&)){ fun(val); };
}

被称为

call(mutableValue)(func)

然而,另一个缺点是 lambda 可能由调用方存储,并在捕获的引用不再有效时意外使用。

您可以在宏调用中隐藏它,以便语法与您想要的语法匹配并减少误用的可能性。