您想为某些函数模仿类似析构函数的行为。
模仿析构函数行为的最聪明的方法是使用析构函数。
我的意思是在你的类的一些并行层次结构的析构函数中调用这些函数。
首先你需要知道基类——我尝试使用 __direct_bases——gcc 的新扩展——但是如果失败了 (gcc bug),那么让我们使用 C++ 解决方案:
template <typename T>
using GetBaseType = typename T::Base;
using NoBase = void;
以上要求此层次结构中的每个类都必须具有Base typedef - 这可以在未来的 C++ 版本中解决。
所以有了基类 - 我们可以创建两个层次结构 - 一个用于非常量,一个用于 const 函数:
template <typename T>
class CallUp;
template <>
class CallUp<NoBase>
{
public:
template <typename Op, typename T>
CallUp(Op&& ignoreOperation, T&& ignoreObject) {}
};
template <typename T>
class CallUp : public CallUp<GetBaseType<T>>
{
public:
template <typename Op>
CallUp(Op&& op, T& obj) : CallUp<GetBaseType<T>>(op, obj), op(std::forward<Op>(op)), obj(obj)
{}
~CallUp()
{
op(obj);
}
private:
std::function<void(T&)> op;
T& obj;
};
template <typename T>
class ConstCallUp;
template <>
class ConstCallUp<NoBase>
{
public:
template <typename Op, typename T>
ConstCallUp(Op&& ignoreOperation, T&& ignoreObject) {}
};
template <typename T>
class ConstCallUp : public ConstCallUp<GetBaseType<T>>
{
public:
template <typename Op>
ConstCallUp(Op&& op, const T& obj) : ConstCallUp<GetBaseType<T>>(op, obj), op(std::forward<Op>(op)), obj(obj)
{}
~ConstCallUp()
{
op(obj);
}
private:
std::function<void(T&)> op;
const T& obj;
};
template <typename Op, typename T>
auto callUp(Op&& op, T& obj)
{
return CallUp<T>(std::forward<Op>(op), obj);
}
template <typename Op, typename T>
auto callUp(Op&& op, const T& obj)
{
return ConstCallUp<T>(std::forward<Op>(op), obj);
}
在 callUp 层次结构的析构函数中 - 你会得到你想要的行为。
如何调用这些析构函数 - 只需定义该类型的临时函数 - 它将立即被销毁。
查看使用示例:
class Foo0
{
public:
using Base = NoBase;
void reset()
{
std::cout << "reset Foo0\n";
}
void print() const
{
std::cout << "Foo0\n";
}
};
class Foo1 : public Foo0
{
public:
using Base = Foo0;
void reset()
{
std::cout << "reset Foo1\n";
}
void print() const
{
std::cout << "Foo1\n";
}
};
class Foo2 : public Foo1
{
public:
using Base = Foo1;
void reset()
{
std::cout << "reset Foo2\n";
}
void print() const
{
std::cout << "Foo2\n";
}
};
int main() {
Foo2 foo2;
callUp([](auto&& obj){obj.reset();}, foo2);
callUp([](auto&& obj){obj.print();}, foo2);
}
输出:
reset Foo2
reset Foo1
reset Foo0
Foo2
Foo1
Foo0
ideone link.
这个框架可以扩展。
例如。多基类可以这样完成:
template <typename ...T>
struct MultiBases {};
template <typename ...T>
class CallUp<MultiBases<T...>> : public CallUp<T>...
{
public:
template <typename Derived, typename Op>
CallUp(Op&& op, Derived& obj) : CallUp<T>(op, obj)...
{}
};
template <typename ...T>
class ConstCallUp<MultiBases<T...>> : public ConstCallUp<T>...
{
public:
template <typename Derived, typename Op>
ConstCallUp(Op&& op, Derived& obj) : ConstCallUp<T>(op, obj)...
{}
};
及用法示例:
class Foo01
{
public:
using Base = NoBase;
void reset();
void print() const;
};
class Foo02
{
public:
using Base = NoBase;
void reset();
void print() const;
};
class Foo1 : public Foo01, public Foo02
{
public:
using Base = MultiBases<Foo01, Foo02>;
void reset();
void print() const;
};
class Foo2 : public Foo1
{
public:
using Base = Foo1;
void reset();
void print() const;
};
int main() {
Foo2 foo2;
callUp([](auto&& obj){obj.reset();}, foo2);
callUp([](auto&& obj){obj.print();}, foo2);
}
输出:
reset Foo2
reset Foo1
reset Foo02
reset Foo01
Foo2
Foo1
Foo02
Foo01
Ideone link
以非虚方式调用虚函数的问题可以在lambda级别解决。
通过这个object_class_t 类模板,我们可以获得对象的“原始”类类型:
template <typename T>
using object_class_t = std::remove_cv_t<std::remove_reference_t<T>>;
所以,假设我们有任何虚拟重置函数 - 并且希望以非虚拟方式调用它们(期望的行为):
int main() {
Foo2 foo2;
auto nonVirtualReset = [](auto&& obj)
{
using ObjClass = object_class_t<decltype(obj)>;
obj.ObjClass::reset();
};
callUp(nonVirtualReset, foo2);
callUp([](auto&& obj){obj.print();}, foo2);
}