确保模板 class 不是多态的?
Ensuring a template class isn't polymorphic?
我最近在开发一个 C++ 库,我在其中设计了一个模板 class,出于效率和安全原因,该模板需要特别是非多态的。为了确保以后我不会忘记这一点而无意中破坏了一切,我想我会做一个好公民并为此添加一个静态断言。
我最初尝试过这样的事情:
template <typename T> class VirtualVerboten {
...
static_assert(!std::is_polymorphic<VirtualVerboten>::value,
"This should not be polymorphic."); // Error!
};
这无法编译,因为在我使用 VirtualVerboten
时,它是一个不完整的类型。如果这是一个非模板 class,我只是将 static_assert
放在类型之后:
class NonTemplateVirtualVerboten {
...
}
static_assert(!std::is_polymorphic<NonTemplateVirtualVerboten>::value,
"This should not be polymorphic.");
但是因为这是一个模板 class,所以制作 "template static_assert
" 的类似想法是不合法的:
template <typename T> class VirtualVerboten {
...
};
template <typename T>
static_assert(!std::is_polymorphic<VirtualVerboten>::value,
"This should not be polymorphic."); // Error!
我想到的解决方案是在 VirtualVerboten
中找到一个可能在实例化模板时使用的成员函数(特别是构造函数),然后将静态断言放在那里:
template <typename T> class VirtualVerboten {
VirtualVerboten();
};
template <typename T>
VirtualVerboten<T>::VirtualVerboten() {
static_assert(!std::is_polymorphic<VirtualVerboten>::value,
"This should not be polymorphic."); // Yay!
doSomeActualThingsAtRuntime();
}
这是有效的,除了它依赖于这样一个事实,即这个特定的构造函数将被实际调用并因此被实例化,如果有多个构造函数可以被调用,它就会失败。
是否有 "foolproof" 方法可以在此处添加此静态断言?我理解为什么原始代码会产生错误以及为什么不能有模板静态断言,所以这更像是 "did I miss another way of doing this?" 而不是 "here's why what you did doesn't work."
final
关键字是一个 C++14 特性,它不允许 classes 被继承。如果 class 被继承,代码将无法编译。示例:
template <typename T>
class VirtualVerboten final {
...
}
如果有人试图继承它...
class Derived : public VirtualVerboten<int> {
...
}
编译器会报错
已经指出了。最好的方法是在析构函数中使用 static_assert
。即
template <typename T>
class VirtualVerboten {
public:
~VirtualVerboten() {
static_assert(!std::is_polymorphic<VirtualVerboten>::value,
"This should not be polymorphic.");
}
};
因为析构函数只能是 1,所以保证只要实例化它的对象就会检查 static_assert
。
IMO,另一种优雅的方法是创建一个实用程序 class 并继承它,例如:
template<typename T>
struct NonPolymorphic
{
~NonPolymorphic()
{ static_assert(!std::is_polymorphic<T>::value, "This should not be polymorphic."); }
};
template <typename T>
class VirtualVerboten : NonPolymorphic<VirtualVerboten<T>>
{
// ...
};
我最近在开发一个 C++ 库,我在其中设计了一个模板 class,出于效率和安全原因,该模板需要特别是非多态的。为了确保以后我不会忘记这一点而无意中破坏了一切,我想我会做一个好公民并为此添加一个静态断言。
我最初尝试过这样的事情:
template <typename T> class VirtualVerboten {
...
static_assert(!std::is_polymorphic<VirtualVerboten>::value,
"This should not be polymorphic."); // Error!
};
这无法编译,因为在我使用 VirtualVerboten
时,它是一个不完整的类型。如果这是一个非模板 class,我只是将 static_assert
放在类型之后:
class NonTemplateVirtualVerboten {
...
}
static_assert(!std::is_polymorphic<NonTemplateVirtualVerboten>::value,
"This should not be polymorphic.");
但是因为这是一个模板 class,所以制作 "template static_assert
" 的类似想法是不合法的:
template <typename T> class VirtualVerboten {
...
};
template <typename T>
static_assert(!std::is_polymorphic<VirtualVerboten>::value,
"This should not be polymorphic."); // Error!
我想到的解决方案是在 VirtualVerboten
中找到一个可能在实例化模板时使用的成员函数(特别是构造函数),然后将静态断言放在那里:
template <typename T> class VirtualVerboten {
VirtualVerboten();
};
template <typename T>
VirtualVerboten<T>::VirtualVerboten() {
static_assert(!std::is_polymorphic<VirtualVerboten>::value,
"This should not be polymorphic."); // Yay!
doSomeActualThingsAtRuntime();
}
这是有效的,除了它依赖于这样一个事实,即这个特定的构造函数将被实际调用并因此被实例化,如果有多个构造函数可以被调用,它就会失败。
是否有 "foolproof" 方法可以在此处添加此静态断言?我理解为什么原始代码会产生错误以及为什么不能有模板静态断言,所以这更像是 "did I miss another way of doing this?" 而不是 "here's why what you did doesn't work."
final
关键字是一个 C++14 特性,它不允许 classes 被继承。如果 class 被继承,代码将无法编译。示例:
template <typename T>
class VirtualVerboten final {
...
}
如果有人试图继承它...
class Derived : public VirtualVerboten<int> {
...
}
编译器会报错
static_assert
。即
template <typename T>
class VirtualVerboten {
public:
~VirtualVerboten() {
static_assert(!std::is_polymorphic<VirtualVerboten>::value,
"This should not be polymorphic.");
}
};
因为析构函数只能是 1,所以保证只要实例化它的对象就会检查 static_assert
。
IMO,另一种优雅的方法是创建一个实用程序 class 并继承它,例如:
template<typename T>
struct NonPolymorphic
{
~NonPolymorphic()
{ static_assert(!std::is_polymorphic<T>::value, "This should not be polymorphic."); }
};
template <typename T>
class VirtualVerboten : NonPolymorphic<VirtualVerboten<T>>
{
// ...
};