在当前的标准草案(和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从指向另一个的指针获得指向一个对象的指针.
当前的标准草案明确指出放置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[]
,但这个问题更多的是关于"为什么?".
标准定义了哪个volatile变量可以不被检测到?
我发现了两个关于volatile的规范性文本:
读取由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中的相同. - 最后的注释]
看看这个小片段:
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
可能不被允许删除的事实是程序中某处可能存在某种类型,它来源于A
和B
.
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) 在任何情况下,由于问题无法用旧的方式表达,因此必须使用尾随返回类型吗?
auto fn() -> int;
可以轻松转换为旧方法:int fn();
。
我想知道是否有一个示例,其中这种转换是不可能的。当我们在返回类型中引用函数参数时,最直观的示例似乎可以通过使用来解决declval
。
注意:这里不要考虑lambda,我们必须在其中使用尾随返回类型。
注意:这个问题是关于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) 看看这个简单的代码(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)
哪个编译器是正确的,为什么?
在2011年之前有一个类似的问题:标准委员会关心的异域架构
现在,我想问一个非常相似的问题,但这一次,我是从程序员的角度和C++ 11的角度来看问题.
目前存在哪些硬件,它有一个C++ 11编译器,可以被认为是异国情调?
我认为异国情调?
所以我们在x86/ARM世界看到的任何不是标准的东西,我们在那里:
注意:我想得到答案,其中硬件存在符合C++ 11标准的编译器,而不存在C++编译器,但不完全符合.
我问这个,因为很多时候,我得到的答案就像"你不能依赖它,它是实现定义的",而且我想知道,实际上,在现实世界中,我可以依赖多少在标准上.举个例子:每当我写作时std::uint16_t
,我可能会担心(因为这个功能是可选的),在平台上,这种类型是不存在的.但是,是否存在这种类型不存在的实际平台?
该标准说明了dcl.constexpr/6中的模板constexpr函数/构造函数:
如果constexpr函数模板的实例化模板特化或类模板的成员函数无法满足constexpr函数或constexpr构造函数的要求,那么该特化仍然是constexpr函数或constexpr构造函数,即使调用这样的函数不能出现在常量表达式中.如果模板的特化不满足constexpr函数或constexpr构造函数在被视为非模板函数或构造函数时的要求,则模板格式错误,无需诊断.
有趣的是:
无法满足... constexpr构造函数的要求,该特化仍然是... constexpr构造函数
因此,即使构造函数被标记constexpr
,也不能在常量表达式中使用它.
为什么存在这个规则?constexpr
当功能不满足要求时,为什么不删除?
目前的行为在两个方面很糟糕:
constexpr
默默地删除.constexpr
构造函数),将动态初始化,没有任何错误/警告(因为构造函数不是"真正"constexpr).这条规则是否有一些优点,这可以平衡它的缺点?