从派生类调用时推断'this'指针类型?
Infer 'this' pointer type when called from derived class?
在基类中有一个方法,它需要传递给它的类型来进行一些与类型相关的操作(查找、大小和一些方法调用)。目前看起来是这样的:
class base
{
template<typename T>
void BindType( T * t ); // do something with the type
};
class derived : public base
{
void foo() { do_some_work BindType( this ); }
};
class derivedOther : public base
{
void bar() { do_different_work... BindType( this ); }
};
但是,我想知道是否有一种方法可以获得调用者的类型,而不必传递这个,以便我的调用点代码变成:
class derived : public base
{
void foo() { BindType(); }
};
没有显式this指针。我知道我可以显式地将模板参数提供为BindType<derived>()
,但是是否有一种方法可以以某种方式提取调用者的类型?
没有什么神奇的方法可以获得调用者的类型,但是您可以使用CRTP(正如注释所提到的)来自动化此行为,代价是代码的复杂性:
class base
{
template<typename T>
void BindType(); // do something with the type
};
template <class T>
class crtper : base
{
void BindDerived { BindType<T>(); }
}
class derived : public crtper<derived>
{
void foo() { do_some_work BindDerived(); }
};
class derivedOther : public crtper<derivedOther>
{
void bar() { do_different_work... BindDerived(); }
};
编辑:我应该提到,我有点期望foo
将是一个虚函数,在base
中定义而不实现。这样,您就可以直接从界面触发操作。虽然你可能在你的实际代码中有,但在你的例子中没有。无论如何,这个解决方案与这个完全兼容。
Edit2:问题编辑后,编辑澄清解决方案仍然适用
如果您想避免使用BindType<derived>()
,请考虑(我同意,这有点啰嗦)BindType<std::remove_reference<decltype(*this)>::type>();
以避免传递参数。它在编译时得到解决,避免了运行时的惩罚。
class base
{
protected:
template<typename T>
void BindType() { cout << typeid(T).name() << endl; } // do something with the type
};
class derived : public base
{
public:
void foo()
{
BindType<std::remove_reference<decltype(*this)>::type>();
}
};
它不会像你期望的那样工作
foo()
的结果可能与您期望的不同:
class derived : public base // <= YOU ARE IN CLASS DERIVED
{
public:
void foo() { BindType( this ); } // <= SO this IS OF TYPE POINTER TO DERIVED
};
模板参数在编译时扣除,因此它将是derived*
。如果从derived
派生的类derived_once_more
调用foo()
,它仍然会使用类型derived*
。
但是你可以去掉虚拟参数*
可以使用decltype(this)
来表示变量的类型名。它仍然在编译时定义:
class base
{
public:
template<typename T>
void BindType( )
{
cout << typeid(T*).name()<<endl; // just to show the type
}
virtual ~base() {}; // for typeid() to work
};
class derived : public base
{
public:
void foo() { BindType<decltype(this)>( ); }
};
在线演示编辑:其他替代
由于模板参数需要在编译时而不是运行时提供,因此可以使用:
- 模板参数扣除(你的第一个代码片段)
-
decltype (see above)
- 如果你打算在所有的派生类中添加这个,你可以使用一个宏来完成它,使用上面提到的解决方案之一
- 可以使用CRTP模式(已经在另一个答案中解释过了)。
避免CRTP中间类的可能解决方案如下:
class base {
using func_t = void(*)(void *);
template<typename T>
static void proto(void *ptr) {
T *t = static_cast<T*>(ptr);
(void)t;
// do whatever you want...
}
protected:
inline void bindType() {
func(this);
}
public:
template<typename T>
base(T *): func{&proto<T>} {}
private:
func_t func;
};
struct derived1: base {
derived1(): base{this} {}
void foo() {
// ...
bindType();
}
};
struct derived2: base {
derived2(): base{this} {}
void bar() {
// ...
bindType();
}
};
int main() {
derived1 d1;
d1.foo();
derived2 d2;
d2.bar();
}
基本思想是利用派生类的构造函数中的this
指针是所需类型的这一事实。
它们可以作为基类构造函数的参数传递,并用于专门化一个函数模板,该函数模板在幕后完成的脏工作。
一旦构造函数返回,派生类的类型实际上在基类中被擦除。无论如何,proto
的专门化包含了这些信息,并且它可以将基类的this
指针强制转换为正确的类型。
只要有几个函数需要专门化,这就可以很好地工作。
在这种情况下,只有一个函数,所以它很好地适用于这个问题。
您可以添加static_assert
来在T
上添加约束,例如:
template<typename T>
base(T *t): func{&proto<T>} {
static_assert(std::is_base_of<base, T>::value, "!");
}
它要求包含<type_traits>
头
- 为什么使用 "this" 指针调用派生成员函数?
- 关于C++中具有多重继承"this"指针的说明
- Doees the 'this' 指针参与虚函数的多态行为
- 为什么成员函数内的"this"指针为空?
- "this"指针的值在对象的生存期内是否恒定?
- 如何将'this'指针传递给C++ WinAPI 线程?
- 无法"this"指针传递到另一个类并在 CPP 中调用该类的任何方法
- 'this'指针是否可以在 c++ 标头声明中使用?
- 为什么在通过引用返回运算符分配时取消引用'this'指针?
- 在类方法中使用 "this" 指针是否是一种好的做法?
- 为什么直接传递"this"指针来存档是一个错误,而另一个相同类型的指针是可以的?
- C++虚拟方法,不需要"this"指针 - 优化
- 指向成员函数签名的指针中缺少"this"指针
- 是否可以获取'this'指针的地址?
- 静态类成员不是与 this 指针没有关联吗?
- 为什么有必要将"this"指针作为"arg"参数传递给pthread_create
- 编译错误:2 个重载没有'this'指针的合法转换。使用结构
- 对象的"this"指针是否等于指向其(单个)基类的指针?
- Do=在lambda的捕获列表中捕获this指针
- 将"this"指针传递给析构函数中的其他类/函数