根据C++标准,具有虚函数的类不能具有普通的复制构造函数:
如果不是用户提供的,则类X的复制/移动构造函数是微不足道的
- 类X没有虚函数(10.3),没有虚基类(10.1),和
- 选择复制/移动每个直接基类子对象的构造函数是微不足道的,并且
- 对于类类型(或其数组)的X的每个非静态数据成员,选择复制/移动该成员的构造函数是微不足道的;
否则复制/移动构造函数是非平凡的.
现在,想象一个类层次结构,它满足除"无虚函数"条件之外的所有上述条件:
struct I
{
virtual void f() = 0;
};
struct D final : I
{
void f() override
{
}
};
Run Code Online (Sandbox Code Playgroud)
从实现的角度来看,这些类只包含指向虚拟调度表的指针.并且基类没有任何用户声明的构造函数,并且派生类是final,因此提到的指针总是可以具有常量值.鉴于这一切,复制构造函数可以是微不足道的,但标准明确禁止对其进行处理.
另一方面,满足了对诸如可破坏的类别进行处理的条件.该类不声明虚拟析构函数并使用隐式定义的析构函数.
要求简单的析构函数看起来像强制优化 - 在这种情况下,隐式定义的析构函数不应该恢复指向虚拟表的指针.
但是在我看来,这样的优化是中途的; 具有虚函数的类仍然无法进行memcopied,即使它们可以被轻易破坏.
问题:
我是否有任何理由从实现角度考虑为什么这些类应该有非平凡的复制构造函数?
是否有任何理由不能在标准中放宽对复制构造函数的琐碎限制?
std :: shared_ptr有一个别名构造函数,允许新创建的shared_ptr在指向某个其他对象时与现有共享指针共享状态.
我在考虑滥用这个构造函数来将指针放到shared_ptr中的一些全局对象:
int global = 0;
int main()
{
// because we point to global object we do not need to track its lifetime
// so we use empty shared_ptr<void> as a provider of shared state
std::shared_ptr<int> p(std::shared_ptr<void>(), &global);
std::shared_ptr<int> pp = p;
return *pp;
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:合法吗?该代码成功地适用于主要编译器.
请注意,我不会问这是否是一件好事.我知道有一种规范的方法可以使用no-op deleter将指针指向全局对象到shared_ptr中.如果它是合法的,它也有点令人不安,因为它可能有可解除引用的shared_ptr,弱指针总是过期的:
std::shared_ptr<int> p(std::shared_ptr<void>(), &global);
std::weak_ptr<int> w = p;
if (p) // p is alive and well
{ // and w is not
*w.lock(); // and here program crashes
}
Run Code Online (Sandbox Code Playgroud) 假设我们想要重载函数模板f,但仅在尚未声明类似的重载时:
template<typename T>
void f(T); // main prototype
struct A {};
struct B {};
//we want to declare B f(A), but only if something like A f(A) hasn't been declared
//we can try to check the type of expression f(A) before defining it
//and disable overload via enable_if
template<typename T = void> //it has to be a template to use enable_if
std::enable_if_t<std::is_same_v<void, decltype(T(), (f)(A{}))>, B> f(A);
// decltype expression depends on T, but always evaluates to the type …Run Code Online (Sandbox Code Playgroud) 我在玩 C++ 模板类型推导并设法编译了这个小程序。
template<typename T>
void f(const T& val)
{
val = 1;
}
int main()
{
int i = 0;
f<int&>(i);
}
Run Code Online (Sandbox Code Playgroud)
它可以在所有主要编译器上编译,但我不明白为什么。为什么可以f赋值val,什么时候val显式标记const?它是这些编译器中的错误还是根据 C++ 标准的有效行为?