C++ 为什么调用总是取消引用数据成员,而不是在可能的情况下调用?
这个问题()询问为什么INVOKE讨论数据成员,而忽略它们实际上是如何被调用的 这个问题()讨论了为什么要访问它们,但为什么在可调用时不调用它们 : 定义调用(f,t1,t2,…,tN),如下所示:C++ 为什么调用总是取消引用数据成员,而不是在可能的情况下调用?,c++,language-lawyer,c++17,C++,Language Lawyer,C++17,这个问题()询问为什么INVOKE讨论数据成员,而忽略它们实际上是如何被调用的 这个问题()讨论了为什么要访问它们,但为什么在可调用时不调用它们 : 定义调用(f,t1,t2,…,tN),如下所示: (1.1)(t1.*f)(t2,…,tN)当f是指向类T的成员函数的指针且为uu-of v为真时 (1.2)(t1.get()*f)(t2,…,tN)当f是指向类T的成员函数的指针,而remove_-cvref_uuuuuut是引用包装器的特殊化 (1.3)((*t1)。*f)(t2,…,tN)当
- (1.1)(t1.*f)(t2,…,tN)当f是指向类T的成员函数的指针且为uu-of v为真时李>
- (1.2)(t1.get()*f)(t2,…,tN)当f是指向类T的成员函数的指针,而remove_-cvref_uuuuuut是引用包装器的特殊化李>
- (1.3)((*t1)。*f)(t2,…,tN)当f是指向类T的成员函数的指针,且t1不满足前两项时李>
- (1.4)t1.*f,当N==1且f是指向类T的数据成员的指针且为“基”of“v”时为真李>
- (1.5)当N==1且f是指向类T的数据成员的指针时,t1.get().*f,而removecvrefT是引用包装器的特殊化李>
- (1.6)(*t1)。*f,当N==1且f是指向类T的数据成员的指针且t1不满足前两项时李>
- (1.7)在所有其他情况下的f(t1,t2,…,tN)
f
拥有操作符()
,则调用所讨论的对象
为什么会有这样的限制?它有什么作用
下面是一些需要澄清的代码:
#include <functional>
#include <iostream>
struct func1
{
void operator()() { std::cout << "Invoked functor\n"; }
};
void func2()
{
std::cout << "Invoked free function\n";
}
struct D1 {};
struct T1 {
func1 f1;
void func3() { std::cout << "Invoked member function\n"; }
D1 d1;
};
int main()
{
T1 t1;
func1 free_f1;
std::invoke(&T1::f1, t1); //does nothing
std::invoke(&func1::operator(), t1.f1); //okay, so there is a workaround, if clumsy
std::invoke(&func2); //calls func2
std::invoke(&T1::func3, t1); //calls func3
std::invoke(&T1::d1, t1); //does nothing (expected)
std::invoke(free_f1); //works on non-member functors
return 0;
}
#包括
#包括
结构函数1
{
void operator(){std::cout在组装C++11的标准库时,它具有来自各种Boost库的许多功能。在本次对话中,以下Boost工具很重要:
bind
功能
reference\u包装器
mem\u fn
在某种程度上,它们都与一个可调用的东西相关,这个东西可以接受一些类型的参数,并产生一个特定的返回值。因此,它们都试图以一致的方式处理可调用的东西。C++11在采用这些工具时,根据完美的转发规则发明了调用的概念,以便所有这些都能够参考一致的处理方式
mem\u fn
的唯一目的是获取指向成员的指针,并将其转换为可直接使用()
。对于指向成员函数的指针,最明显的做法是调用所指向的成员函数,给定该函数的对象和任何参数。对于指向成员变量的指针,最明显的做法是返回该成员变量的值,给定要访问的对象
将数据成员指针转换为返回变量本身的一元函子的功能非常有用。您可以使用类似于std::transform
的方法,将数据成员指针的mem\u fn
传递给它,以生成访问特定可访问数据成员的值序列。添加到C++20的范围功能将使其更加有用,因为您可以创建变换范围,只需获取成员指针即可操作子对象序列
但问题是:无论成员子对象的类型如何,您都希望它能够工作。如果该子对象恰好是可调用的,mem\u fn
应该能够像访问任何其他对象一样访问可调用对象。它恰好是可调用的事实与mem\u fn
的用途无关
但是boost::function
和boost::bind
可以使用成员指针。它们都基于boost::mem\u fn
的成员指针行为。因此,如果mem\u fn
将指向数据成员的指针视为返回该成员值的一元函子,那么所有这些都必须将指向数据成员的指针视为一个数据指针那样的话
因此,当C++11将所有这些编码成一个统一的概念时,它将实践直接编码成INVOKE
所以从词源学的角度来看,这就是为什么INVOKE
是这样工作的:因为所有这些都意味着以相同的方式对待可调用项,mem_fn
关于数据成员指针的全部要点是将它们视为返回其值的一元函数。所以其他人也必须这样对待它们
这难道不是一件好事吗?这不是正确的行为吗?如果成员指针指向的类型恰好可以调用,那么您真的想让指向数据成员的指针的行为与不可调用的指针的行为大不相同吗?这将使编写接受数据成员指针的通用代码变得不可能,然后对某些序列进行操作对象。如果您不确定数据成员指针是否会得到引用的子对象或调用子对象本身,那么如何才能常规地访问该数据成员指针?一般来说,数据不是可以调用的对象。@eerorika存储的lambda和函数对象可以。它对所有类型的成员都有意义,而不仅仅是对函子。这是在链接的问题中解释的,所以我认为链接的问题是完全重复的,不是吗?您可以执行struct X{int n;};std::function f(&X::n);
现在,f(X)
返回X.n
。注意&X::n
是指向数据成员的普通指针,而不是可调用的指针。这就是用法(1.4)
支持。(1.5)
和(1.6)
留给读者作为练习。@IgorTandetnik是的,这在链接的问题中,但这并不能让我更接近这个问题的答案。