【发布时间】:2019-01-26 00:26:42
【问题描述】:
这个问题 (Why does INVOKE facility in the C++11 standard refer to data members?) 询问为什么 INVOKE 讨论了数据成员,却忽略了它们的实际调用方式。
这个问题 (What is std::invoke in c++?) 讨论了为什么它们会被访问,但为什么它们不被调用如果可调用。
定义 INVOKE(f, t1, t2, ..., tN) 如下:
- (1.1) (t1.*f)(t2, …, tN) 当 f 是指向类 T 的成员函数的指针并且 is_base_of_v
> 为真时;李> - (1.2) (t1.get().*f)(t2, ..., tN) 当 f 是指向类 T 的成员函数的指针并且 remove_cvref_t
是 reference_wrapper 的特化时; - (1.3) ((*t1).*f)(t2, …, tN) 当f是一个指向类T的成员函数的指针并且t1不满足前两项时;
- (1.4) t1.*f 当 N == 1 且 f 是指向类 T 的数据成员且 is_base_of_v
> 为 true 时; - (1.5) t1.get().*f 当 N == 1 且 f 是指向类 T 的数据成员的指针且 remove_cvref_t
是 reference_wrapper 的特化; - (1.6) (*t1).*f 当 N == 1 且 f 是指向类 T 的数据成员的指针且 t1 不满足前两项时;
- (1.7) f(t1, t2, ..., tN) 在所有其他情况下。
1.4 到 1.6 处理对指向数据成员的指针的访问,这对于给定函子和存储的可调用对象是有意义的。我不明白的是为什么它不调用这些成员,而是简单地取消引用它们?如果f 拥有operator (),我希望1.4 将与1.1 的语法和呃...invoke有问题的对象平行。
为什么会有这个限制,它的目的是什么?
这里有一些代码澄清:
#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;
}
这编译得很好,但只在第二次调用invoke 时调用func1()。我理解为什么 INVOKE 在第一个参数不是可调用对象时什么都不做。我的问题是为什么标准不允许调用指向数据成员的可调用指针,即为什么标准不要求在上述std::invoke 的第一次使用中调用f1?
编辑:由于std::invoke 是在 C++17 中添加的,因此我将这个问题标记为这样,希望参与该过程的人能够有所启发。
这是用于添加std::invoke() 的original paper,它实际上解释了它想要统一处理仿函数的动机:
虽然 INVOKE 表达式的行为可以通过现有标准库组件的组合来重现,但在此类解决方案中需要对函子和成员指针进行单独处理。
在上面的代码中,您可以看到这是可行的……只是不适用于指向本身是函子的成员数据的指针。这只是一个疏忽吗?
【问题讨论】:
-
数据一般不是可以调用的。
-
@eerorika 可以存储 lambdas 和函数对象。
-
它对所有类型的成员都有意义,而不仅仅是函子。这在链接的问题中有解释,所以我认为链接的问题是完全重复的,不是吗?
-
你可以做
struct X{int n;}; std::function<int(X&)> f(&X::n);现在,f(x)返回x.n。请注意,&X::n是一个指向数据成员的普通指针,而不是可调用的。这就是(1.4)支持的用法。(1.5)和(1.6)留给读者作为练习。 -
@IgorTandetnik 是的,这在链接的问题中,但它并没有让我更接近这个问题的答案。
标签: c++ language-lawyer c++17