小编Bla*_*ger的帖子

为什么placement-new表达式不能是常量表达式?

根据[expr.const]/5.18

表达式 E 是核心常量表达式,除非对 E 的求值遵循抽象机 ([intro.execution]) 的规则,将求值以下其中一项:

  • new 表达式 ([expr.new]),除非所选分配函数是可替换的全局分配函数 ([new.delete.single]、[new.delete.array]),并且分配的存储空间在E;

放置新表达式不是常量表达式。

为了解决这个问题,C++20 添加了std::construct_at. 那么为什么不能将placement-new表达式设为常量表达式呢?

c++ placement-new constant-expression c++20

15
推荐指数
2
解决办法
570
查看次数

使用模板化运算符删除时出现虚拟析构函数问题

以下类(带有虚拟析构函数)包含模板化的operator delete

\n
struct S\n{\n    virtual ~S() {}\n    template <typename... Args>\n    void operator delete(void* ptr, Args... args);\n};\n
Run Code Online (Sandbox Code Playgroud)\n

args可以为空,所以我认为S::operator delete也可以在预期常规时使用delete

\n

但是(使用 g++),我收到错误\xef\xbc\x9a

\n
\n

错误:“S”没有合适的“操作符删除”

\n
\n

难道“合适的‘操作删除’”就不能成为一个模板吗?

\n

c++ delete-operator virtual-destructor

10
推荐指数
1
解决办法
382
查看次数

为什么某些 C++ 标准函数缺少文字异常规范?

有些函数应该是非抛出的,但标准对此没有任何说明。例如erase(q)q表示有效的可解引用常量迭代器)的关联容器。根据[res.on.exception.handling#4],这允许实现抛出任何标准异常:

C++ 标准库中定义的函数没有 Throws: 段落,但具有潜在抛出异常规范,可能会抛出实现定义的异常。170 实现应通过抛出标准异常类或派生自标准异常类的异常来报告错误([坏.alloc]、[support.exception]、[std.exceptions])。

因此,如果您想吞掉它们抛出的任何实现定义的异常,则必须使用 try-catch 块。

std::set<int> s{1};
try
{
    s.erase(s.cbegin());
}
catch (...) {}
Run Code Online (Sandbox Code Playgroud)

它丑陋且低效,但却是必要的。所以我也不知道这样做有什么好处。

c++ exception language-lawyer noexcept

6
推荐指数
1
解决办法
486
查看次数

在不是从通常的 new 中获得的指针上使用删除表达式的合法性

我想知道这段代码是否合法:

delete new (operator new(1)) char;
Run Code Online (Sandbox Code Playgroud)

此代码执行相同的操作,但不在从放置 new 获得的指针上使用删除表达式:

void* p = operator new(1);
new (p) char;
delete static_cast<char*>(p);
Run Code Online (Sandbox Code Playgroud)

[expr.delete#2]的标准规则:

在单对象删除表达式中,delete 操作数的值可以是空指针值、由先前的非数组 new 表达式产生的指针值,或者指向由以下方法创建的对象的基类子对象的指针:如此新的表达方式。如果不是,则行为未定义。在数组删除表达式中,delete 操作数的值可以是空指针值或由前一个数组 new-expression 产生的指针值。68 如果不是,则行为未定义。

该标准并没有说 new-express 不包括放置 new。所以我认为按照标准,第一段代码应该是合法的,但第二段代码应该是非法的。我的想法正确吗?

为什么标准这么说?调用operator new并在其上构造一个对象,其作用与new表达式几乎相同,但在获得的指针上使用delete表达式不一定合法。

对于分配数组,标准似乎允许这样做:

delete[] new (operator new[](2)) char[2];
Run Code Online (Sandbox Code Playgroud)

但由于未指定的开销,它应该是非法的。

为什么标准允许这样做?


编辑:根据[intro.object]/13

对名为operator new 或operator new[] 的函数的任何隐式或显式调用都会在返回的存储区域中隐式创建对象,并返回指向合适的已创建对象的指针。

所以我什至可以编写以下代码:

delete static_cast<char*>(operator new(1));
Run Code Online (Sandbox Code Playgroud)

它销毁隐式创建的char对象并释放内存,执行与上面示例相同的操作。但根据标准,它绝对是非法的,因为没有使用 new 表达式。

为什么标准不允许这样做?

c++ memory-management placement-new language-lawyer delete-operator

6
推荐指数
1
解决办法
219
查看次数

为什么 std::valarray 辅助类的某些赋值运算符返回 void?

例如,赋值运算符std::slice_array

void operator=(const valarray<T>&) const; //#1
void operator=(const T&) const; //#2
const slice_array& operator=(const slice_array&) const; //#3
Run Code Online (Sandbox Code Playgroud)

#1#2返回void,但#3返回const slice_array&

它禁止某些代码,例如:

std::valarray<int> va{1, 2, 3, 4, 5, 6};
va[std::slice(3, 2, 2)] = va[std::slice(0, 2, 2)] = va[0];
Run Code Online (Sandbox Code Playgroud)

为什么?

c++ proxy-classes assignment-operator valarray

6
推荐指数
1
解决办法
165
查看次数

当对象的初始化创建的新表达式通过抛出异常终止时调用释放函数的查找

下面的代码:

#include <new>
#include <iostream>
#include <cstdlib>
struct alignas(32) S
{
    S() {throw 1;}
    void* operator new(std::size_t count, std::align_val_t al)
    {
        return ::operator new(count, al);
    }
    void operator delete(void* ptr, std::align_val_t al)
    {
        std::cerr << "aligned delete\n";
        ::operator delete(ptr, al);
    }
};
int main()
{
    try
    {
        S* ps = new S;
    }
    catch(...)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

将输出aligned delete.

当然这并没有什么问题。但是,在我添加模板删除后:

struct alignas(32) S
{
    ...
    template <typename... Args>
    void operator delete(void* ptr, Args... args)
    {
        std::cerr << "templated …
Run Code Online (Sandbox Code Playgroud)

c++ new-operator language-lawyer delete-operator

5
推荐指数
1
解决办法
141
查看次数

std::views::istream 与 std::views::take

我用 g++ 12.2.1 编译了以下代码:

#include <iostream>
#include <ranges>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
    std::vector<int> vi;
    std::ranges::copy(std::views::istream<int>(std::cin) | std::views::take(3), std::back_inserter(vi));
    for (auto i : vi)
        std::cout << i << ' ';
}
Run Code Online (Sandbox Code Playgroud)

输入:

1 2 3
4
Run Code Online (Sandbox Code Playgroud)

输出:1 2 3

为什么我必须输入 4 个数字而不是 3 个数字并丢弃最后一个数字?怎么解决?

c++ c++20 std-ranges

3
推荐指数
1
解决办法
103
查看次数

(多字类型名称){表达式列表}合法吗?

考虑:

\n
(long long){1};\n
Run Code Online (Sandbox Code Playgroud)\n

It\xe2\x80\x99s 不是C 风格的转换表达式每个 [expr.cast]/1 的

\n
\n

表达式 (T) 强制转换表达式的结果为 T 类型。如果 T 是左值引用类型或对函数类型的右值引用,则结果是左值;如果 T 是对对象类型的右值引用,则结果是 xvalue;否则结果是纯右值。

\n
\n
\n

强制转换表达式:

\n
    \n
  • 一元表达式
  • \n
  • ( type-id ) 强制转换表达式
  • \n
\n
\n

{1}不应该是强制转换表达式

\n

它也不是每个 [expr.type.conv]/1 的函数式转换表达式:

\n
\n

简单类型说明符或类型名说明符后跟带括号的可选表达式列表或大括号初始化列表(初始化程序),在给定初始化程序的情况下构造指定类型的值。如果该类型是推导类类型的占位符,则它将替换为本子条款其余部分的类模板推导重载决策所选择的函数的返回类型。否则,如果类型包含占位符类型,则将其替换为由占位符类型推导 ([dcl.type.auto.deduct]) 确定的类型。

\n
\n
\n

简单类型说明符:

\n
    \n
  • 嵌套名称说明符 opt 类型名称
  • \n
  • 嵌套名称说明符模板简单模板 ID
  • \n
  • decl类型说明符
  • \n
  • 占位符类型说明符
  • \n
  • 嵌套名称说明符 opt 模板名称
  • \n
  • 字符
  • \n
  • char8_\xc2\xadt
  • \n
  • char16_\xc2\xadt
  • \n …

c++ grammar casting language-lawyer braced-init-list

1
推荐指数
1
解决办法
108
查看次数

为什么析构函数的语法是~classname?

析构函数的语法是~classname. 这导致需要在析构函数调用中显式写入对象的类型。为了避免这种情况,C++17 引入了std::destroy_at.

那么,Bjarne Stroustrup 选择~classname析构函数语法的最初理由是什么?如果语法不依赖于类型,std::destroy_at则不需要。

c++ destructor language-design explicit-destructor-call

-1
推荐指数
2
解决办法
185
查看次数