在智能指针上调用成员函数指针

5
处理类成员函数指针时,我们可以使用以下语法在对象实例上调用函数:
struct X {
    void foo();
};

X x;                // Instance
auto f = &X::foo;   // Member function pointer

(x.*f)();           // Call function f on x

当一个实例有一个原始指针时,语法是这样的:
X *xptr = new X();

(xptr->*f)();

这很好地遵循了静态函数调用的类比:
x.foo();
x->foo();

然而,当我拥有该类的一个智能指针时,这不起作用:

unique_ptr<X> xsptr(new X());

(xsptr->*f)();    // g++: error: no match for 'operator->*' in 'xsptr ->* f'

我通过先使用解引用运算符,然后使用.语法进行调用来解决这个问题。
((*xsptr).*f)();

这有多丑陋?

编译器是否应该拒绝上述的箭头语法?因为通常情况下(在静态调用函数时),它会调用 operator ->。难道 ->* 也不应该只调用 operator -> 吗?

编译器错误部分回答了这个问题:错误提示表明我们可以重载 operator ->* 以便在取消引用对象后调用成员函数指针,但是 unique_ptr 却没有这样做。但这引发了更多的问题。如果语言要求这样做,为什么智能指针却没有这样做呢?是因为这样做会引入其他问题吗?为什么我们必须编写此运算符,而不是隐式地调用 -> 返回的对象上的成员函数指针呢?(例如,当编写 xsptr->foo() 时的行为)


@Xeo 我的意思是,标准完全可以为 ->* 定义一个默认行为,即“在 operator-> 返回的对象上调用成员指针”,类似于以下内容:operator ->*(memptr) { return ((*this)->).*memptr(); }。但由于它 没有 这样做,那么为什么 C++11 中的指针类型没有定义这个运算符呢?有原因吗? - leemes
啊,我猜这也适用于C++03。 - leemes
封装它,不再考虑:call(xsptr, f, args); - Peter Wood
那只是基本的想法。我从未实现过成员调用运算符,所以我不知道。也许可变模板参数? - leemes
是的,请自己实现call - Peter Wood
显示剩余3条评论
2个回答

3
编译器拒绝上述箭头语法是正确的吗?因为通常(当静态调用函数时),它会调用operator ->。- > *也不应该调用operator ->(根据标准)。请注意,尽管语法可能看起来相似,但- > *是它本身的运算符,而不是- >与其他* thing的组合。

非常好,目前一切顺利。我刚刚学会了这个,谢谢! :) 但是:如何始终解决这个问题呢?假设我有一个模板参数 T,它可以是原始指针或智能指针,t 是该实例,f 是要在 t 上调用的指针。我目前编写的是 ((*t).*f)() 这是很丑陋的,但似乎可以工作。 - leemes
@leemes:auto& obj = *p; (obj.*f)(stuff...) - Xeo
@leemes:如果只是针对单个表达式,你还在意什么呢?此外,对于这个例子,std::bind(f, p, stuff...)()将变成((*t).*f)(stuff...) - Xeo
@leemes:你无法这样做。你需要使用语言提供的设施,没有办法模仿operator->operator.*之后的操作。虽然智能指针可以通过模板在内部提供类似的功能,但它会产生代码膨胀(对于每个类类型中的每个成员类型,都会有一个新函数用于此)并且提供的好处很少(成员指针在真实代码中并不是非常常见),而且它也不是operator->*。整个指向成员的语法和语义有点奇怪,大多数人甚至不关心 :) - David Rodríguez - dribeas
1
@leemes:我改正了,你可以将其实现为数据成员的 operator->*。但我不确定是否可以对非数据成员(即函数成员)进行操作,因为我无法弄清楚从重载的 operator->* 返回的类型应该是什么(对于指针 U* 和指向成员的指针 T U::*,返回值被定义为 T&,但在成员函数的情况下并不完全适用)。 - David Rodríguez - dribeas
显示剩余2条评论

1
你可以为智能指针重载operator->*。请参考Scott Meyers的this文章。基本思想是返回一个函数对象,该对象将在从操作符返回后被调用。
这篇文章有点过时,现在你可以使用一个可变函数参数数量的可变模板。

当然可以!你只需要在类外定义它。例如,对于 unique_ptr,代码应该像这样:template const PMFC operator->*(const std::unique_ptr &pObj, ReturnType (T::*pmf)()) { return std::make_pair(pObj.get(), pmf); } - Artyom Chirkov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接