是否可以在调用后自动推断指向三元中成员重载函数的指针的类型?

Posted

技术标签:

【中文标题】是否可以在调用后自动推断指向三元中成员重载函数的指针的类型?【英文标题】:Is it possible to automatically deduce the type of the pointer to member overloaded function in ternary when called after? 【发布时间】:2021-01-02 05:33:31 【问题描述】:

所以我有这样的场景,我想调用一个类的任一函数,其中所讨论的函数具有相同的原型,但它也被重载。因为我知道指向成员的指针,所以我的第一反应是这样的:

struct test

    int overloaded(char) 
    int overloaded(int) 
    int overloadedone(char) 
    int overloadedone(int) 
 test;

int main()

    (test.*(true ? (&test::overloaded) : (&test::overloadedone)))(1);

然而事实证明编译器(MSVC - 2019 Preview latest version with std C++ preview)无法推断出类型,我必须写:

(test.*(true ? static_cast<int (test::*)(int)>(&test::overloaded) : static_cast<int (test::*)(int)>(&test::overloadedone)))(1);

反而让我回到了过去:

true ? test.overloaded(1) : test.overloadedone(1);

但我想知道这是否是要求这些演员的定义行为。甚至:

(test.*static_cast<int (test::*)(int)>(true ? (&test::overloaded) : (&test::overloadedone)))(1);

没用。

你必须像第二个例子一样,对三元的两种可能性中的每一种都写下所说的演员表。

【问题讨论】:

不存在指向重载的成员函数集的指针。必须执行重载决议,并获取特定重载的地址。该标准定义了几种可能的上下文:在强制转换中,或在初始化或赋值的右侧;强制转换的类型或左侧的类型指导重载解决方案。您正在尝试在这些特定上下文之外获取重载成员函数的地址。 表达式(true ? (&amp;test::overloaded) : (&amp;test::overloadedone))) 不起作用,因为overloadedoverloadedone 各有两个候选者,每个候选者都有不同的类型。结果是模棱两可。在将 static_cast 应用于结果之前,需要明确评估该表达式。 你可以这样做:godbolt.org/z/bnrM44 @PatrickRoberts 不幸的是,这是我正在使用的库,无法控制。 【参考方案1】:

这不是特别优雅,但是如果在传递成员函数指针本身之前对成员函数指针的参数进行柯里化,这种方法可以推断出重载:

#include <iostream>

template <class... Args>
auto invoke_conditional_mem_fn(Args... args)

    return [=] <class R, class X> (X x, bool b, R(X::*t)(Args...), R(X::*f)(Args...)) -> R
    
        return (x.*(b ? t : f))(args...);
    ;


struct test

    int overloaded(char)  std::cout << "overloaded(char) "; return 1; 
    int overloaded(int)  std::cout << "overloaded(int) "; return 2; 
    int overloadedone(char)  std::cout << "overloadedone(char) "; return 3; 
    int overloadedone(int)  std::cout << "overloadedone(int) "; return 4; 
 test;

int main()

    std::cout
        << invoke_conditional_mem_fn('1')(test, true, &test::overloaded, &test::overloadedone)
        << std::endl
        << invoke_conditional_mem_fn(1)(test, false, &test::overloaded, &test::overloadedone)
        << std::endl;

感谢@dyp 和their example,我们知道如果我们选择要传递的参数,我们可以推断出成员函数指针的返回类型和基数。


或者,如果它满足您的需求,您可以做一些像这样更简单的事情。只需声明一个 lambda 即可使用 ifelse 语句解决三元表达式的限制,因为三元运算符的每个分支都必须属于同一类型。

#include <iostream>

struct test

    int overloaded(char)  std::cout << "overloaded(char) "; return 1; 
    int overloaded(int)  std::cout << "overloaded(int) "; return 2; 
    int overloadedone(char)  std::cout << "overloadedone(char) "; return 3; 
    int overloadedone(int)  std::cout << "overloadedone(int) "; return 4; 
 test;

auto conditional = [] (struct test& test, bool cond, auto... args)

    if (cond) return test.overloaded(args...);
    else return test.overloadedone(args...);
;

int main()

    std::cout << conditional(test, true, '1') << std::endl;
    std::cout << conditional(test, false, 1) << std::endl;

【讨论】:

你可以通过两步推导来推导返回类型来改进吗?如果您有参数类型,则可以在选择重载时推断其余部分:compiler-explorer.com/z/n3hG1G 嗯,只是从美学的角度来看,我会将成员函数与其他参数分开:invoke_conditional(test, true, '1')(&amp;test::overloaded, &amp;test::overloadedone) @dyp 我认为这并不重要,我会让 OP 选择他们希望如何分隔他们的论点,因为与其他方法相比,这样做相当微不足道。 @dyp 这可能吗? 啊,我不知道为什么我认为它是,显然不是,好点。我会删除该评论。 @PatrickRoberts 如果您有两个仅在返回类型上有所不同的函数,代码无法编译。 C++ 从不使用返回类型为转换运算符选择重载异常,但随后无论如何,你不能有相同的名字!

以上是关于是否可以在调用后自动推断指向三元中成员重载函数的指针的类型?的主要内容,如果未能解决你的问题,请参考以下文章

指向成员转换的指​​针

这个指向成员转换的指​​针有啥问题?

虚函数的调用

错误:'指向成员的指针对托管类无效'

指向重载静态成员的函数指针 - 在 unique_ptr 中用作自定义删除器

类中的函数重载