我在基类中有一个方法,它需要传递给它的类型用于某些与类型相关的操作(查找,大小和一些方法调用)。目前它看起来像这样:
event = soup.find('td', {'id': "highlight_today"})
det = event.select_one("span.econoitems")
name, an = det.text, det.a["href"]
print(name, an )
但是,我想知道是否有一种获取调用者类型的方法,而不必传递它,以便我的调用点代码变为:
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 ); }
};
没有显式的this指针。我知道我可以明确地将模板参数提供为class derived : public base
{
void foo() { BindType(); }
};
,但有没有办法以某种方式以某种方式提取调用者的类型?
答案 0 :(得分:4)
获取调用者的类型没有神奇的方法,但您可以使用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
中没有实现定义。这样,您就可以直接从界面触发操作。虽然你可能在实际代码中有这个,但在你的例子中没有。无论如何,这个解决方案与此完全兼容。
编辑2:编辑问题后,编辑以澄清该解决方案仍然适用。
答案 1 :(得分:3)
如果您想避免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>();
}
};
答案 2 :(得分:2)
它无法正常工作
foo()
的结果可能与您的期望不同:
class derived : public base // <= YOU ARE IN CLASS DERIVED
{
public:
void foo() { BindType( this ); } // <= SO this IS OF TYPE POINTER TO DERIVED
};
模板参数在编译时时扣除,因此它将为derived*
。如果您要从foo()
派生的类derived_once_more
致电derived
,则仍会使用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)
答案 3 :(得分:2)
避免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>
标题。