有没有办法只声明一个指向特定方法的指针?
Is there a way to declare a pointer to a particular method only?
我有一个 class:
class Car
{
public:
Car();
~Car();
// nr indicates which particular door.
void startEngie();
void openDoor(int nr);
void closeDoor(int nr);
void openWindow(int nr);
void closeWindow(int nr);
void turnOnAirConditioner(int vloume);
}
现在 void (Car::*action)(int)
我声明了一个名为 action
的指向 Car
方法的指针。问题是很多方法都可以赋值给它:&Car::openDoor
、&Car::closeDoor
、&Car::openWindow
、&Car::closeWindow
、&Car::turnOnAirConditioner
,但不是&Car::startEngie
。
我想知道我是否正确:没有办法只声明一个指向特定方法的指针。换句话说:pointer type
,它只接受一个特定的命名函数,不能被声明。
这种指针void (Car::*action)(int)
很少用到。
现在有三种首选方法来解决这个问题:
- 模板参数(以STL算法为例)
- 接口(纯抽象class)
std::function<void()>
和/或 lambdas
显然您需要第二种解决方案:
class IWindowOpenable {
public:
virtual ~IWindowOpenable() {}
virtual void openWindow(int nr) = 0;
};
class Car : public IWindowOpenable
{
public:
Car();
~Car();
void startEngie();
void openDoor(int nr);
void closeDoor(int nr);
void openWindow(int nr);
void closeWindow(int nr);
void turnOnAirConditioner(int vloume);
};
Car car;
IWindowOpenable *windowOpener = &Car;
请注意,任何实现接口 IWindowOpenable
的东西都只能在名称为 openWindow
的方法上运行。
该指针可以指向实现该接口的任何拼写错误的对象。
评论里说你问的是纯学术问题,那我就纯学术回答吧。您不能只有一个指向一个方法的指针,因为所有这些方法都具有相同的类型。你可以做的是让它们有不同的类型:
struct Car
{
struct open_door_tag { };
struct close_door_tag { };
void open_left_door(int nr, open_door_tag = {});
void open_right_door(int nr, open_door_tag = {});
void close_door(int nr, close_door_tag = {});
};
现在如果你有指针
void (Car::*open_that_door_ptr)(int, Car::open_door_tag);
您对分配有一些限制:
open_that_door_ptr = &Car::open_left_door; // OK
open_that_door_ptr = &Car::open_right_door; // OK
//open_that_door_ptr = &Car::close_door; // Won't compile
不幸的是(或不是),如果不提供标记,您将无法通过此指针调用方法:
Car car;
(car.*open_that_door_ptr)(2, Car::open_door_tag{});
// or (thanks, @Jarod42)
(car.*open_that_door_ptr)(2, {});
// (car.*open_that_door_ptr)(2); // Won't compile
注意。在我发布这个答案后,我看到@Jarod42 的评论,他建议做基本相同的事情。
您不能定义 是 只是 "one specific value of this other type" 的类型,例如,其唯一现有值恰好是 &Car::closeDoor
.[=15 的类型=]
你可以做的一件事是拿起编程的瑞士军刀——引入一个间接级别——并定义一个代表[=29的类型=] 一个特定的值,将其包裹在其他东西中。
// Simplified Car
class Car
{
public:
void openDoor(int i) { std::cout << "open door\n"; }
void closeWindow(int i) { std:cout << "close window\n"; }
};
using Fun = void (Car::*)(int);
// A template for an immutable structure, so it
// can hold only one specific value; the template argument.
template<Fun fun>
struct CarOperation
{
const Fun f = fun;
};
// Now we can make one type for each operation
using OpenDoor = CarOperation<&Car::openDoor>;
using CloseWindow = CarOperation<&Car::closeWindow>;
int main()
{
Car c;
OpenDoor d;
(c.*d.f)(1);
CloseWindow e;
(c.*e.f)(2);
}
语法有点不幸,因为显然 .*
运算符不对其右操作数进行任何隐式转换。
当然,这只是简单调用类型指示的函数的一种非常迂回和混淆的方式。
使用可以包含函数的预定子集的类型(例如,DoorOperation
或 Opener
类型)会更有用。
您可能可以使用聪明的模板编程来构建它,但我不是一个聪明的模板程序员。
尽管 Marek 提出了考虑使用更多 C++ 惯用替代方案的正确建议,但您的愿望是合理的,可以通过简单的 const
.
实现
人们可能想要这样做的原因与所有其他 const 定义(原则上都可以用其定义的右侧替换)相同:人们想要一个符号名称,以便具体的值在其使用的上下文中是可以理解的,并且人们希望有一个单点维护以防万一想要更改 const
值。这就是为什么每个人都对代码中的整数文字皱眉。
这些确切的原因同样适用于 const int
和 void (Car::*const action)(int)
的定义。
因此,举一个人为的例子,如果您想要一个每次汽车启动时都会调用的函数,作为该特定汽车的不可变 属性,您可以声明一个 atStart
常量成员函数指针作为成员(我也引入了一个typedef)。指针必须在汽车的构造函数中初始化,所有 const 成员都必须如此。
#include<iostream>
using namespace std;
struct Car
{
typedef void (Car::* const CarfuncT)(void);
Car(CarfuncT atStartArg)
: atStart(atStartArg)
{}
void startEngine() { cout << "Start Engine\n"; (this->*atStart)(); };
void turnOnAC() { cout << "AC on\n"; }
void turnOnHeat() { cout << "Heat on\n"; }
void turnOnRadio() { cout << "Radio on\n"; }
void (Car::* const atStart)();
};
用户代码可能如下所示:
int main()
{
cout << "I'll create a configured car. In which state do you live?\n";
string state;
cin >> state;
Car *car = new Car(state == "Texas" ? &Car::turnOnAC : &Car::turnOnRadio);
car->startEngine();
delete car;
}
我有一个 class:
class Car
{
public:
Car();
~Car();
// nr indicates which particular door.
void startEngie();
void openDoor(int nr);
void closeDoor(int nr);
void openWindow(int nr);
void closeWindow(int nr);
void turnOnAirConditioner(int vloume);
}
现在 void (Car::*action)(int)
我声明了一个名为 action
的指向 Car
方法的指针。问题是很多方法都可以赋值给它:&Car::openDoor
、&Car::closeDoor
、&Car::openWindow
、&Car::closeWindow
、&Car::turnOnAirConditioner
,但不是&Car::startEngie
。
我想知道我是否正确:没有办法只声明一个指向特定方法的指针。换句话说:pointer type
,它只接受一个特定的命名函数,不能被声明。
这种指针void (Car::*action)(int)
很少用到。
现在有三种首选方法来解决这个问题:
- 模板参数(以STL算法为例)
- 接口(纯抽象class)
std::function<void()>
和/或 lambdas
显然您需要第二种解决方案:
class IWindowOpenable {
public:
virtual ~IWindowOpenable() {}
virtual void openWindow(int nr) = 0;
};
class Car : public IWindowOpenable
{
public:
Car();
~Car();
void startEngie();
void openDoor(int nr);
void closeDoor(int nr);
void openWindow(int nr);
void closeWindow(int nr);
void turnOnAirConditioner(int vloume);
};
Car car;
IWindowOpenable *windowOpener = &Car;
请注意,任何实现接口 IWindowOpenable
的东西都只能在名称为 openWindow
的方法上运行。
该指针可以指向实现该接口的任何拼写错误的对象。
评论里说你问的是纯学术问题,那我就纯学术回答吧。您不能只有一个指向一个方法的指针,因为所有这些方法都具有相同的类型。你可以做的是让它们有不同的类型:
struct Car
{
struct open_door_tag { };
struct close_door_tag { };
void open_left_door(int nr, open_door_tag = {});
void open_right_door(int nr, open_door_tag = {});
void close_door(int nr, close_door_tag = {});
};
现在如果你有指针
void (Car::*open_that_door_ptr)(int, Car::open_door_tag);
您对分配有一些限制:
open_that_door_ptr = &Car::open_left_door; // OK
open_that_door_ptr = &Car::open_right_door; // OK
//open_that_door_ptr = &Car::close_door; // Won't compile
不幸的是(或不是),如果不提供标记,您将无法通过此指针调用方法:
Car car;
(car.*open_that_door_ptr)(2, Car::open_door_tag{});
// or (thanks, @Jarod42)
(car.*open_that_door_ptr)(2, {});
// (car.*open_that_door_ptr)(2); // Won't compile
注意。在我发布这个答案后,我看到@Jarod42 的评论,他建议做基本相同的事情。
您不能定义 是 只是 "one specific value of this other type" 的类型,例如,其唯一现有值恰好是 &Car::closeDoor
.[=15 的类型=]
你可以做的一件事是拿起编程的瑞士军刀——引入一个间接级别——并定义一个代表[=29的类型=] 一个特定的值,将其包裹在其他东西中。
// Simplified Car
class Car
{
public:
void openDoor(int i) { std::cout << "open door\n"; }
void closeWindow(int i) { std:cout << "close window\n"; }
};
using Fun = void (Car::*)(int);
// A template for an immutable structure, so it
// can hold only one specific value; the template argument.
template<Fun fun>
struct CarOperation
{
const Fun f = fun;
};
// Now we can make one type for each operation
using OpenDoor = CarOperation<&Car::openDoor>;
using CloseWindow = CarOperation<&Car::closeWindow>;
int main()
{
Car c;
OpenDoor d;
(c.*d.f)(1);
CloseWindow e;
(c.*e.f)(2);
}
语法有点不幸,因为显然 .*
运算符不对其右操作数进行任何隐式转换。
当然,这只是简单调用类型指示的函数的一种非常迂回和混淆的方式。
使用可以包含函数的预定子集的类型(例如,DoorOperation
或 Opener
类型)会更有用。
您可能可以使用聪明的模板编程来构建它,但我不是一个聪明的模板程序员。
尽管 Marek 提出了考虑使用更多 C++ 惯用替代方案的正确建议,但您的愿望是合理的,可以通过简单的 const
.
人们可能想要这样做的原因与所有其他 const 定义(原则上都可以用其定义的右侧替换)相同:人们想要一个符号名称,以便具体的值在其使用的上下文中是可以理解的,并且人们希望有一个单点维护以防万一想要更改 const
值。这就是为什么每个人都对代码中的整数文字皱眉。
这些确切的原因同样适用于 const int
和 void (Car::*const action)(int)
的定义。
因此,举一个人为的例子,如果您想要一个每次汽车启动时都会调用的函数,作为该特定汽车的不可变 属性,您可以声明一个 atStart
常量成员函数指针作为成员(我也引入了一个typedef)。指针必须在汽车的构造函数中初始化,所有 const 成员都必须如此。
#include<iostream>
using namespace std;
struct Car
{
typedef void (Car::* const CarfuncT)(void);
Car(CarfuncT atStartArg)
: atStart(atStartArg)
{}
void startEngine() { cout << "Start Engine\n"; (this->*atStart)(); };
void turnOnAC() { cout << "AC on\n"; }
void turnOnHeat() { cout << "Heat on\n"; }
void turnOnRadio() { cout << "Radio on\n"; }
void (Car::* const atStart)();
};
用户代码可能如下所示:
int main()
{
cout << "I'll create a configured car. In which state do you live?\n";
string state;
cin >> state;
Car *car = new Car(state == "Texas" ? &Car::turnOnAC : &Car::turnOnRadio);
car->startEngine();
delete car;
}