小编gez*_*eza的帖子

static_cast'd指针值

在当前的标准草案(和C++ 17)中,是关于static_casting的void *:

类型"指针的prvalue CV1空隙"可以被转换成类型的prvalue"指针CV2 T",其中T是一个对象类型和CV2是相同的CV-资格,或更大的CV-资格比,CV1.如果原始指针值表示存储器中字节的地址A而A不满足T的对齐要求,则未指定结果指针值.否则,如果原始指针值指向对象a,并且存在类型为T(忽略cv-qualification)的对象b,该对象b是指针可互换,则结果是指向b的指针.否则,转换指针值不变.

我想知道,转换是指针可互换的有什么区别?有没有一种情况,当一个void *东西指向一个指针 - 可互换实际上改变指针值?这种区别的意图是什么?

对于完整性指针可互换:

如果出现以下情况,则两个对象a和b是指针可互换的:

  • (4.1)它们是同一个对象,或者
  • (4.2)一个是union对象,另一个是该对象的非静态数据成员([class.union]),或者
  • (4.3)一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则该对象的任何基类子对象([class. mem]),或
  • (4.4)存在对象c,使得a和c是指针可互换的,并且c和b是指针可互换的.

如果两个对象是指针可互换的,则它们具有相同的地址,并且可以通过reinterpret_cast从指向另一个的指针获得指向一个对象的指针.

c++ void-pointers static-cast language-lawyer c++17

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

安置新的开销[]

当前的标准草案明确指出放置new[]可能有空间开销:

这种开销可以应用于所有数组新表达式,包括那些引用库函数operator new [](std :: size_t,void*)和其他放置分配函数的表达式.开销的数量可能因新的一次调用而异.

所以大概他们有一些想法,为什么编译器需要这个开销.它是什么?编译器可以将此开销用于任何有用的东西吗?

在我的理解中,要破坏这个数组,唯一的解决方案是在一个循环中调用析构函数(我是否正确?),因为没有放置delete[](顺便说一下,我们不应该有delete[]正确的方法来破坏数组,不仅仅是它的元素?).因此编译器不必知道数组长度.

我认为这个开销不能用于任何有用的东西,编译器不会使用它(所以这在实践中不是问题).我用这个简单的代码检查了编译器:

#include <stdio.h>
#include <new>

struct Foo {
    ~Foo() { }
};

int main() {
    char buffer1[1024];
    char buffer2[1024];

    float *fl = new(buffer1) float[3];
    Foo *foo = new(buffer2) Foo[3];

    printf("overhead for float[]: %d\n", (int)(reinterpret_cast<char*>(fl) - buffer1));
    printf("overhead for Foo[]  : %d\n", (int)(reinterpret_cast<char*>(foo) - buffer2));
}
Run Code Online (Sandbox Code Playgroud)

GCC和clang根本不使用任何开销.但是,MSVC使用8个字节的Foo情况.为什么MSVC会使用这种开销?


这是一些背景,为什么我提出这个问题.

之前有关于此主题的问题:

据我所知,这些问题的道德是避免使用放置new[],并new在循环中使用放置.但是这个解决方案不会创建一个数组,但是使用彼此相邻的元素(不是数组)operator[]是未定义的行为.这些问题更多的是关于如何避免安置new[],但这个问题更多的是关于"为什么?".

c++

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

标准定义了volatile变量可以改变的位置?

标准定义了哪个volatile变量可以不被检测到?

我发现了两个关于volatile的规范性文本:

intro.execution/7:

读取由volatile glvalue([basic.lval])指定的对象,修改对象,调用库I/O函数或调用执行任何这些操作的函数都是副作用,这些都是状态的变化.执行环境.表达式(或子表达式)的评估通常包括值计算(包括确定用于glvalue评估的对象的身份以及获取先前分配给用于prvalue评估的对象的值)和启动副作用.当对库I/O函数的调用返回或通过volatile glvalue进行访问时,即使调用所隐含的某些外部操作(例如I/O本身)或易失性访问,也会认为副作用已完成可能尚未完成.

这段是关于未被发现的变化吗?请问副作用这个意思?


或者有dcl.type.cv/5:

通过volatile glvalue进行访问的语义是实现定义的.如果尝试通过使用非易失性glvalue访问使用volatile限定类型定义的对象,则行为未定义.

这个段落是关于我的问题吗?什么"通过volatile glvalue进行访问的语义是实现定义的"究竟是什么意思?你能举一个不同的"访问语义"的例子吗?


还有dcl.type.cv/6,这是关于我的问题,但它只是一个注释:

[注意:volatile是对实现的暗示,以避免涉及对象的激进优化,因为对象的值可能会被实现无法检测到的方式更改.此外,对于某些实现,volatile可能指示访问对象需要特殊的硬件指令.有关详细语义,请参阅[intro.execution].一般来说,volatile的语义在C++中与在C中的相同. - 最后的注释]

c++ volatile language-lawyer c++17

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

当编译器没有看到可以实现转换的可能类型时,是否允许编译器优化volatile指针的dynamic_cast?

看看这个小片段:

struct A {
    virtual ~A() { }
};

struct B { };

bool fn() {
    A *volatile a = new A;
    return dynamic_cast<B *>(a); 
}
Run Code Online (Sandbox Code Playgroud)

编译器是否允许dynamic_cast完全删除,并转换dynamic_cast为简单nullptr;

这个问题的原因是这个答案.

笔记:

  • 假设volatile意味着编译器不能假设任何东西a,因为它是volatile.这是一个问题的原因.

  • dynamic_cast可能不被允许删除的事实是程序中某处可能存在某种类型,它来源于AB.

c++ dynamic-cast volatile language-lawyer

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

std :: add_lvalue_reference和std :: add_rvalue_reference的目的是什么?

std::add_lvalue_reference和的目的是std::add_rvalue_reference什么?

似乎使用T &/可以T &&做到这一点,因为它可以成功编译:

#include <utility>

int main() {
    { using T = int;    static_assert(std::is_same_v<std::add_lvalue_reference_t<T>, T &>); };
    { using T = int &;  static_assert(std::is_same_v<std::add_lvalue_reference_t<T>, T &>); };
    { using T = int &&; static_assert(std::is_same_v<std::add_lvalue_reference_t<T>, T &>); };

    { using T = int;    static_assert(std::is_same_v<std::add_rvalue_reference_t<T>, T &&>); };
    { using T = int &;  static_assert(std::is_same_v<std::add_rvalue_reference_t<T>, T &&>); };
    { using T = int &&; static_assert(std::is_same_v<std::add_rvalue_reference_t<T>, T &&>); };
}
Run Code Online (Sandbox Code Playgroud)

c++

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

必须使用尾随返回类型的示例,因为无法通过旧方法解决问题

在任何情况下,由于问题无法用旧的方式表达,因此必须使用尾随返回类型吗?

auto fn() -> int;可以轻松转换为旧方法:int fn();

我想知道是否有一个示例,其中这种转换是不可能的。当我们在返回类型中引用函数参数时,最直观的示例似乎可以通过使用来解决declval

注意:这里不要考虑lambda,我们必须在其中使用尾随返回类型。

c++

9
推荐指数
2
解决办法
141
查看次数

有哪些命名空间以及规则是什么?

注意:这个问题是关于name space,而不是namespace

C++ 标准有一些对 的引用name space,但我没有看到它的定义。标准说标签和宏位于不同的命名空间中。所有其他引用name space都在 C/C++ 兼容性部分,如下所示(当前草案):

这是 C 和 C++ 之间为数不多的可归因于新的 C++ 名称空间定义的不兼容性之一,其中名称可以在单个范围内声明为类型和非类型,导致非类型名称隐藏类型名称并要求使用关键字 class、struct、union 或 enum 来引用类型名称。这个新的命名空间定义为 C++ 程序员提供了重要的符号便利,并有助于使用户定义类型的使用尽可能类似于基本类型的使用。

这个新的命名空间定义是什么?我在哪里可以在标准中找到它?具体规则是什么?规则似乎比“非类型隐藏类型”更复杂。就像,这不会编译:

typedef int Foo; // Foo is a type
void Foo();      // not a type, but compile error, instead of hiding
Run Code Online (Sandbox Code Playgroud)

但这确实:

struct Foo { }; // Foo is a type as well
void Foo();     // This hides the type Foo. "struct …
Run Code Online (Sandbox Code Playgroud)

c++ namespaces

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

部分专业化可以在专业化的参数列表中引用 sizeof(T) 吗?

看看这个简单的代码(godbolt):

template <typename, int>
struct Foo;

template <typename T>
struct Foo<T, (int)sizeof(T)>;
Run Code Online (Sandbox Code Playgroud)

Clang 会编译它,而 gcc 不会:

error: template argument '(int)(sizeof (T))' involves template parameter(s)
    struct Foo<T, (int)sizeof(T)>;
                  ^~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

哪个编译器是正确的,为什么?

c++

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

C++ 11在异国情调的硬件上

在2011年之前有一个类似的问题:标准委员会关心的异域架构

现在,我想问一个非常相似的问题,但这一次,我是从程序员的角度和C++ 11的角度来看问题.

目前存在哪些硬件,它有一个C++ 11编译器,可以被认为是异国情调?

我认为异国情调?

  • 其中char不是8位
  • 其中IEEE 754浮点数格式不可用
  • 其中整数不以两个补码编码
  • 其中编译器不支持8,16或32位类型
  • 内存模型不是线性的(所以你不能比较/减去任何指针)

所以我们在x86/ARM世界看到的任何不是标准的东西,我们在那里:

  • 有8/16/32位两个补码整数
  • IEEE754浮动,一些完全兼容,一些不,但使用IEEE754格式
  • 线性记忆模型

注意:我想得到答案,其中硬件存在符合C++ 11标准的编译器,而不存在C++编译器,但不完全符合.

我问这个,因为很多时候,我得到的答案就像"你不能依赖它,它是实现定义的",而且我想知道,实际上,在现实世界中,我可以依赖多少在标准上.举个例子:每当我写作时std::uint16_t,我可能会担心(因为这个功能是可选的),在平台上,这种类型是不存在的.但是,是否存在这种类型不存在的实际平台?

c++ c++11

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

Constexpr构造函数无法满足要求,但仍然是constexpr.为什么?

该标准说明了dcl.constexpr/6中的模板constexpr函数/构造函数:

如果constexpr函数模板的实例化模板特化或类模板的成员函数无法满足constexpr函数或constexpr构造函数的要求,那么该特化仍然是constexpr函数或constexpr构造函数,即使调用这样的函数不能出现在常量表达式中.如果模板的特化不满足constexpr函数或constexpr构造函数在被视为非模板函数或构造函数时的要求,则模板格式错误,无需诊断.

有趣的是:

无法满足... constexpr构造函数的要求,该特化仍然是... constexpr构造函数

因此,即使构造函数被标记constexpr,也不能在常量表达式中使用它.

为什么存在这个规则?constexpr当功能不满足要求时,为什么不删除?

目前的行为在两个方面很糟糕:

  • 非constexpr-ness不是在最近的可能位置捕获,而是在实际的constexpr表达式中使用它.所以我们必须找到有问题的部分,在那里constexpr默默地删除.
  • 一个对象,打算静态初始化(因为它有一个constexpr构造函数),将动态初始化,没有任何错误/警告(因为构造函数不是"真正"constexpr).

这条规则是否有一些优点,这可以平衡它的缺点?

c++ language-lawyer c++17

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