我正在阅读n3290 C++ 11标准草案(尽可能接近实际标准文本),我注意到会i = i++ + 1;产生未定义的行为.我以前见过类似的问题,但是它们是根据较旧的标准(序列点)来回答的.新标准在表达式和子表达式执行之间的关系之前/之后引入了Sequencing的概念.
1.9 13之前的序列是由单个线程(1.10)执行的评估之间的不对称,传递,成对关系,它在这些评估中引起部分顺序.给定任何两个评估A和B,如果A在B之前被排序,那么A的执行应该在B的执行之前.如果A在B之前没有排序,而B在A之前没有排序,那么A和B是未排序的.[注意:未经测试的评估的执行可能会重叠.-end note]评估A和B是不确定的顺序,当A在A之前测序或B在A之前测序,但未指定哪一个.[注意:不确定顺序的评估不能重叠,但可以先执行. - 尾注]
1.9 14在与要评估的下一个全表达式相关的每个值计算和副作用之前,对与全表达式相关的每个值计算和副作用进行排序.
1.9 15除非另有说明,否则对个体操作员的操作数和个别表达式的子表达式的评估是不确定的.[注意:在程序执行期间不止一次评估的表达式中,不需要在不同的评估中一致地执行对其子表达式的未序列和不确定顺序的评估.-end note]运算符操作数的值计算在运算符结果的值计算之前排序.如果对标量对象的副作用相对于对同一标量对象的其他影响或使用相同标量对象的值进行的值计算未进行排序,则行为未定义.
[ Example:
void f(int, int);
void g(int i, int* v) {
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
f(i = -1, i = -1); // …Run Code Online (Sandbox Code Playgroud) 我在这里读到,如果编译器memset知道传递的内存缓冲区永远不再使用,则可以自由删除调用.怎么可能?在我看来(从核心语言的角度来看)memset只是一个常规函数,编译器无权假设其中发生的任何事情都没有副作用.
在链接文章中,他们展示了如何删除Visual C++ 10 memset.我知道Microsoft编译器在标准合规性方面并不领先,所以我问 - 它是按照标准,还是仅仅是msvc-ism?如果符合标准,请详细说明;)
编辑: @Cubbi
以下代码:
void testIt(){
char foo[1234];
for (int i=0; i<1233; i++){
foo[i] = rand()%('Z'-'A'+1)+'A';
}
foo[1233]=0;
printf(foo);
memset(foo, 0, 1234);
}
Run Code Online (Sandbox Code Playgroud)
在mingw下用线编译:
g++ -c -O2 -frtti -fexceptions -mthreads -Wall -DUNICODE -o main.o main.cpp
g++ -Wl,-s -Wl,-subsystem,console -mthreads -o main.exe main.o
objdump -d -M intel -S main.exe > dump.asm
Run Code Online (Sandbox Code Playgroud)
给出输出:
4013b0: 55 push ebp
4013b1: 89 e5 mov ebp,esp
4013b3: 57 push …Run Code Online (Sandbox Code Playgroud) 阅读这个答案,我很惊讶,如果它真的像所描述的那样工作,那就试试吧:
#include <iostream>
class A {
public:
virtual void foo() = 0;
};
class B {
public:
virtual void foo() = 0;
};
class C : public A, public B {
public:
virtual void foo();
};
void C::foo(){
std::cout << "C" << std::endl;
}
void C::A::foo(){
std::cout << "A" << std::endl;
}
void C::B::foo(){
std::cout << "B" << std::endl;
}
int main() {
C c;
static_cast<A*>(&c)->foo();
static_cast<B*>(&c)->foo();
c.foo();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我真的不认为可以从两个具有相同名称和签名的不同基类重写虚方法.并且,正如我所料,上面的程序打印:
C
C
C
Run Code Online (Sandbox Code Playgroud)
所以答案是错误的 - 正如我从一开始就感觉到的那样.令人惊讶的部分是:为什么我的gcc接受这种语法: …
Qt创建者可以选择将一个窗口小部件推广到从基本窗口小部件派生的自定义创建的类 - 我想用它来将窗口小部件升级到当前项目中的类.Qt创建者问我关于类名和头文件名,这些值直接转到*.ui文件,然后转到ui_myform.h - 问题是这个文件可能(并且通常是)在源代码树外生成(在构建树中) )可以在任意位置,因此在推广窗口中直接指定路径无济于事.如何让QtCreator/uic知道在哪里寻找正确的标题?它甚至可能吗?
也许有一些Qt变量specyfying源树的位置,我可以插入头文件名字段?
我正在使用自编译的QtCreator 2.0.1 +自编Qt 4.7.1.
编辑:
为什么不能只输入头文件的完整路径名?
如果我将移动源代码树,或者甚至在网上共享它,那么每个想要编译我的项目的人都必须在Qt创建者或源文件中编辑这个路径 - 两者都是不可接受的.
为什么这不起作用:
#include <memory>
#include <map>
std::map<std::unique_ptr<char>, std::unique_ptr<int>> foo();
std::map<std::unique_ptr<char>, std::unique_ptr<int>> barmap;
int main(){
barmap=foo();
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这样做:
#include <memory>
#include <map>
std::map<std::unique_ptr<char>, std::unique_ptr<int>> foo();
std::map<std::unique_ptr<char>, std::unique_ptr<int>> barmap;
int main(){
std::map<std::unique_ptr<char>, std::unique_ptr<int>> tmp(foo());
using std::swap;
swap(barmap, tmp);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这与地图中的键类型不可复制这一事实有关(std :: map是否需要?).编译时的相关错误行g++ -std=c++14:
/usr/include/c++/4.9/ext/new_allocator.h:120:4: error: use of deleted function ‘constexpr std::pair<_T1, _T2>::pair(std::pair<_T1, _T2>&&) [with _T1 = const std::unique_ptr<char>; _T2 = std::unique_ptr<int>]’
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^
In file included from /usr/include/c++/4.9/bits/stl_algobase.h:64:0,
from /usr/include/c++/4.9/memory:62,
from pairMove.cpp:1:
/usr/include/c++/4.9/bits/stl_pair.h:128:17: …Run Code Online (Sandbox Code Playgroud) 给定一个需要解析的32位little-endian字段的二进制文件,我想编写正确编译的解析代码,而不依赖于执行该代码的机器的字节顺序.目前我用
uint32_t fromLittleEndian(const char* data){
return uint32_t(data[3]) << (CHAR_BIT*3) |
uint32_t(data[2]) << (CHAR_BIT*2) |
uint32_t(data[1]) << CHAR_BIT |
data[0];
}
Run Code Online (Sandbox Code Playgroud)
然而,这会产生不理想的装配.在我的机器上g++ -O3 -S产生:
_Z16fromLittleEndianPKc:
.LFB4:
.cfi_startproc
movsbl 3(%rdi), %eax
sall $24, %eax
movl %eax, %edx
movsbl 2(%rdi), %eax
sall $16, %eax
orl %edx, %eax
movsbl (%rdi), %edx
orl %edx, %eax
movsbl 1(%rdi), %edx
sall $8, %edx
orl %edx, %eax
ret
.cfi_endproc
Run Code Online (Sandbox Code Playgroud)
为什么会这样?在小端机器上编译时,我怎么能说服它生成最佳代码:
_Z17fromLittleEndian2PKc:
.LFB5:
.cfi_startproc
movl (%rdi), %eax
ret
.cfi_endproc
Run Code Online (Sandbox Code Playgroud)
我通过编译得到的:
uint32_t fromLittleEndian2(const char* data){
return …Run Code Online (Sandbox Code Playgroud) 是否有可能以某种方式禁止对未明确写入特化的类型使用模板化函数.我的意思是这样的
template <typename T>
void foo(){}
template <>
void foo<int>(){}
int main(int argc, char* argv[]){
foo<int>(); //ok
foo<char>(); //Wrong - no specialized version for char.
}
Run Code Online (Sandbox Code Playgroud)
我不能跳过通用版本的函数,因为然后编译器说,当我尝试专门化时,foo不是模板函数.我可以简单地编写一些不能在泛型函数中编译的东西,并添加一些解释原因的注释,但这将是非常无用的信息.我想做的是,能够直接导致编译器出现错误,例如"foo()未定义".
我试图在Linux中用C++获取块设备的一些信息(特别是块大小).是否可以在不安装设备的情况下获得设备的块大小,并且可能无需查看动态文件(如中的那些/sys),但仅限系统调用.
我正在尝试stat,但/dev如果我问的话,它会返回有关文件系统的数据/dev/sdb2.
如果系统调用不可能,我应该在哪里查看动态文件(也无法找到它.)
我知道之前已经问过这个问题,但在你给我一个减号并报告重复的问题之前,请考虑一下这个问题:
在之前的所有答案中,每个人都说对象内存布局依赖于编译器.那怎么样,共享库(*.dll,*.so)可以导出和导入c ++类,即使来自不同的编译器,它们肯定可以组合在一起?考虑一下在mingw下编写的DirectX应用程序.DirectX是使用MSVC++编译的,那么这些环境如何在内存布局上达成一致呢?我知道DirectX在很大程度上依赖于C++类和多态.
提出不同的要求:假设我有一个选择的架构(例如Windows,intel x86),我正在尝试编写一个新的编译器.我如何知道如何访问由另一个编译器编译的.dll lib提供的类实例(vtable,成员字段)?或者就是这样:M $编写了VC++,从那以后它就是不成文的标准,而且出于兼容性原因,其他所有编译器也都这样做了吗?那么linux或其他操作系统呢?
编辑:
好吧我承认,由于COM规范,DirectX的例子很糟糕......
另一个例子:QT.我正在使用QT和mingw,但我知道也有可用的MSVC版本.我不知道差异是仅在标题中,还是共享库(dll-s)也不同.如果它们是,那是否意味着我必须使用包含qt库的应用程序来分发我的应用程序,所以如果有人碰巧有一个用于不同编译器的应用程序,它将不会混淆?(好的记忆和代码共享,对吗?).或者他们是一样的,还有一些不成文的法律呢?
EDIT2:
我已经安装了一个不同的qt版本(msvc 2010),只是为了看看是什么和不共享.似乎共享(他们真的共享)库是不同的.似乎我真的必须在我的应用程序中提供qt-libs然后......这不是一件小事(例如QtGui 8-9MB).那些其他较小的库,其作者不是那么善于为其他编译器提供版本呢?这是否意味着我被他们的原始编译器困住了?如果我想使用由不同编译器编译的两个不同的库,该怎么办?
我已经阅读了很多关于(不)使用throw(X)函数签名的论点,我认为它在ISO C++中指定的方式(并在当前编译器中实现)它相当无用.但是为什么编译器不能简单地在编译时强制执行异常正确性呢?
如果我编写包含throw(A,B,C)在其签名中的函数/方法定义,编译器在确定给定函数的实现是否异常正确时应该没有太多问题.这意味着功能体具有
throw的其他比throw A; throw B; throw C;;throw (A,B,C);,至少在外面try{}catch()捕捉其他抛出的类型.如果编译器在不满足这些要求时引发错误,那么所有函数都应该是"安全的",并且不需要运行时函数unexpected().所有这些都将在编译时得到保证.
void fooA() throw (A){
}
void fooAB() throw (A,B){
}
void fooABC() throw (A,B,C){
}
void bar() throw (A){
throw A(); // ok
throw B(); // Compiler error
fooA(); // ok
fooAB(); // compiler error
fooABC(); // compiler error
try{
throw A(); // ok
throw B(); // ok
throw C(); // Compiler …Run Code Online (Sandbox Code Playgroud) 考虑以下代码:
namespace Foo1 {
void add( int ) {}
void subtract( int ) {}
}
namespace Foo2 {
class Bar {
public:
friend void add( Bar ) {}
};
void subtract( Bar ) {}
void xxx() {
int i = 0;
using namespace Foo1;
add( i ); // Is this an error or not?
subtract( i ); // This is an error due to name hiding
}
}
Run Code Online (Sandbox Code Playgroud)
在中,Foo2::xxx()我使用using命名空间来访问Foo1::add和Foo1::subtract。调用减法显然是一个错误,因为Foo2::subtract
隐藏名称。但是Foo2::add不应真正可见,Foo2 …
c++ ×9
memory ×2
templates ×2
block-device ×1
c ×1
c++11 ×1
c++14 ×1
endianness ×1
exception ×1
layout ×1
linux ×1
optimization ×1
overriding ×1
promoting ×1
qt ×1
qt-creator ×1
stdmap ×1
syntax ×1