为什么我们可以向new表达式传递可选参数,但不能向delete表达式传递可选参数?

4
在stackoverflow上已经有一些关于这个主题的问题了,但我没有看到任何解释删除表达式限制背后的原理。为了澄清这个主题,我尝试在以下三个备注中收集了我理解的事实。
备注1:普通新表达式 我们考虑任何“operator new”(无论是默认全局版,覆盖默认全局版的版本,重载版本还是类成员版本),并假设我们也有一个匹配的“operator delete”。假设原型如下:
void* operator new(size_t, T1, T2, ..., Tn); 
void operator delete(void*, T1, T2, ..., Tn);

我们知道当程序员输入一个新的表达式时:
T* t = new(t1, ..., tn) T(); 

(其中t1,...,tn的类型分别为T1,...,Tn)
编译器会自动将这行代码替换为类似以下的内容:

T* t;
void* raw = operator new(sizeof(T), t1, ..., tn); // or T::operator new(...)
try {
  t = new(raw) T(); 
}
catch (...) {
  operator delete(raw, t1, ..., tn); // or T::operator delete(...)
  throw;
}

备注2:删除表达式-在某些情况下是合法的
我们知道,当程序员键入此删除表达式时:

delete t;

(其中t的类型为T)
编译器会自动将此行替换为类似以下内容的代码:

if (t) {
  t->~T();
  operator delete(t); // or T::operator delete(t);
}

我们知道,通过简单的语法“delete t;”,我们可以隐式并自动调用与默认全局变量相同参数的任何“operator delete”(它可以是默认的全局变量本身,覆盖默认全局变量的版本或一个类成员版本,它采用与默认全局变量相同的参数)。
备注3: 通常情况下不允许使用“delete expression” 现在考虑任何一个“operator delete”,它不采用与默认全局变量相同的参数(它可能是全局重载版本或类成员版本)。 假设原型如下:
void operator delete(void*, T1, ..., Tn);

我们本来想写一个通用的删除表达式:

delete(t1, ..., tn) t;

希望编译器能自动将其替换为:
if (t) {
  t->~T();
  operator delete(t, t1, ..., tn); // or T::operator delete(...);
}

很遗憾,我们知道 "delete(t1, ..., tn) t;" 是非法的

因此,通常建议编写模板函数,例如:

template<typename T> void destroy(T* t, T1 t1, ..., Tn tn) {
    if(t) {
        p->~T();
        operator delete(t, t1, ..., tn); // or T::operator delete(...); ?!
    }
}

这可能会有问题(你必须为每个“operator delete”定义一个,而且你必须小心,因为你不能处理全局版本和类成员版本的方式相同)。

总之:
我们有一个通用的new表达式,但没有通用的delete表达式。 你知道这种看起来不一致背后的原理吗?


你能详细解释一下备注1吗?我看不到类似 void* operator new(size_t, T1, T2, ..., Tn); 的代码,它在这里:http://www.cplusplus.com/reference/new/operator%20new/ - Roman Zaitsev
@RomanZaytsev 这个原型代表了任何可能的“operator new”。例如,它可以代表用户定义的重载版本。 - Flop
1个回答

1
请注意,自C++11和可变参数模板以来,您可以将destroy写成以下形式:
// Helper to define has_operator_delete traits
template <typename T, typename ...Ts> std::false_type has_operator_delete_impl(...);
template <typename T, typename ...Ts> auto has_operator_delete_impl(int)
-> decltype(std::declval<T>().operator delete(nullptr, std::declval<Ts>()...),
            std::true_type{});

// traits to know if T has operator delete(void*, Ts...)
template <typename T, typename ...Ts>
using has_operator_delete = decltype(has_operator_delete_impl<T, Ts...>(0));

然后是destroy函数:
template<typename T, typename ...Ts>
std::enable_if_t<has_operator_delete<T, Ts...>::value>
destroy(T* t, Ts&&... ts)
{
    if (t) {
        t->~T();
        T::operator delete(t, std::forward<Ts>(ts)...);
    }
}

template<typename T, typename ...Ts>
std::enable_if_t<!has_operator_delete<T, Ts...>::value>
destroy(T* t, Ts&&... ts)
{
    if (t) {
        t->~T();
        operator delete(t, std::forward<Ts>(ts)...);
    }
}

演示现场

我不知道为什么没有通用的删除表达式。也许在某个地方解析时会引入歧义...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接