可通过 C++ 中的隐式转换访问的调用成员函数

Call member function that can be reached by implicit conversion in C++

提问人:Israel Yankelovich 提问时间:10/12/2023 更新时间:10/12/2023 访问量:89

问:

尝试创建一个类似于 std::reference_wrapper 的引用包装器,但可以调用包装对象的方法。

#include <iostream>

class A {
public:
    void f() {
         std::cout << "A::f()\n";
    }
};

class WrapA {
public:
    WrapA(A& a) : _a_ptr{&a} {}

    operator A&() {
        return *_a_ptr;
    }
private:
    A* _a_ptr;
};

int main() {
    A a;
    WrapA wrap{a};

    wrap.f();                  // Fails with compile error: ‘class WrapA’ has no member named ‘f’
    static_cast<A>(wrap).f();  // Works

    return 0;
}

为什么在包装器对象上调用类 A 函数 f() 不起作用,尽管可以从 WrapA 隐式转换为 A?有没有办法解决这个问题?

尝试使用 std::reference_wrapper 并失败并出现相同的编译错误。

C++ 隐式转换 引用包装器

评论

4赞 Nathan Pierson 10/12/2023
在弄清楚如何解析对成员函数的调用时,从不考虑转换。 将始终基于(可能是动态的)类型,而不是可以转换为其他对象。foo.some_function()foofoo
1赞 R Sahu 10/12/2023
使用点运算符时不执行隐式转换。

答:

3赞 NathanOliver 10/12/2023 #1

获得此类行为的一种方法是重载箭头运算符并让它返回指向包装对象的指针。编译器将在返回的指针上递归应用该函数,并允许您调用成员函数。那会给你operator ->

#include <iostream>

class A {
public:
    void f() {
         std::cout << "A::f()\n";
    }
};

class WrapA {
public:
    WrapA(A& a) : _a_ptr{&a} {}

    A* operator ->() {
        return _a_ptr;
    }
private:
    A* _a_ptr;
};

int main() {
    A a;
    WrapA wrap{a};

    wrap->f();
    
    return 0;
}

哪个输出

A::f()

你可以在这个实时示例中看到它的工作原理。

评论

0赞 Israel Yankelovich 10/12/2023
是否有可能覆盖运算符点 (.)?所以那行得通吗?wrap.f()
0赞 NathanOliver 10/12/2023
@IsraelYankelovich 不能超载operator .
0赞 R Sahu 10/12/2023 #2

你问

为什么在包装器对象上调用类 A 函数 f() 不起作用,尽管可以从 WrapA 隐式转换为 A?

该标准规定了可以进行隐式转换的上下文 (https://timsong-cpp.github.io/cppwp/n4140/conv#2):

2 [ 注意:在多个上下文中,具有给定类型的表达式将隐式转换为其他类型:

(2.1) -- 用作运算符的操作数时。运算符对其操作数的要求决定了目标类型(子句 [expr])。

(2.2) -- 在 if 语句或迭代语句 ([stmt.select], [stmt.iter]) 的条件下使用时。目标类型为 bool。

(2.3) -- 用于 switch 语句的表达式时。目标类型为整数 ([stmt.select])。

(2.4) -- 用作初始化的源表达式时(包括在函数调用中用作参数和在返回语句中用作表达式)。要初始化的实体的类型(通常)是目标类型。请参见 [dcl.init], [dcl.init.ref]。

尾注 ]

请注意,在点运算符中使用对象不是其中之一。