最近在一次采访中有一个以下客观类型的问题.
int a = 0;
cout << a++ << a;
Run Code Online (Sandbox Code Playgroud)
回答:
一个.10
b.01
c.未定义的行为
我回答了选择b,即输出为"01".
但令我惊讶的是,一位采访者告诉我,正确的答案是选项c:undefined.
现在,我确实知道C++中序列点的概念.以下语句的行为未定义:
int i = 0;
i += i++ + i++;
Run Code Online (Sandbox Code Playgroud)
但根据我对该陈述的理解cout << a++ << a,ostream.operator<<()将被召唤两次,先是ostream.operator<<(a++)后来ostream.operator<<(a).
我还检查了VS2010编译器的结果,其输出也是'01'.
来自http://en.cppreference.com/w/cpp/string/byte/memcpy:
如果对象不是TriviallyCopyable(例如标量,数组,C兼容结构),则行为未定义.
在我的工作中,我们使用std::memcpy了很长时间来按比例交换不是TriviallyCopyable的对象:
void swapMemory(Entity* ePtr1, Entity* ePtr2)
{
static const int size = sizeof(Entity);
char swapBuffer[size];
memcpy(swapBuffer, ePtr1, size);
memcpy(ePtr1, ePtr2, size);
memcpy(ePtr2, swapBuffer, size);
}
Run Code Online (Sandbox Code Playgroud)
从来没有任何问题.
我理解滥用std::memcpy非TriviallyCopyable对象并导致下游的未定义行为是微不足道的.但是,我的问题是:
std::memcpy当与非TriviallyCopyable对象一起使用时,为什么它本身的行为是未定义的?为什么标准认为有必要指定?
UPDATE
http://en.cppreference.com/w/cpp/string/byte/memcpy的内容已经过修改,以回应这篇文章和帖子的答案.目前的描述说:
如果对象不是TriviallyCopyable(例如标量,数组,C兼容结构),则行为是未定义的,除非程序不依赖于目标对象(不运行
memcpy)的析构函数的效果和生命周期目标对象(已结束,但未开始memcpy)由其他一些方法启动,例如placement-new.
PS
@Cubbi的评论:
@RSahu如果有东西保证UB下游,它会使整个程序不确定.但我同意在这种情况下似乎可以绕过UB并相应地修改cppreference.
请参阅此代码段
int main()
{
unsigned int a = 1000;
int b = -1;
if (a>b) printf("A is BIG! %d\n", a-b);
else printf("a is SMALL! %d\n", a-b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这给出了输出:a是SMALL:1001
我不明白这里发生了什么.>运算符如何在这里工作?为什么"a"小于"b"?如果它确实更小,为什么我得到一个正数(1001)作为差异?
当我运行以下程序时
#include <iostream>
int main()
{
char c = 'a';
std::cout << c << std::endl;
std::cout.operator<<(c) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我得到了输出
a
97
Run Code Online (Sandbox Code Playgroud)
在http://en.cppreference.com/w/cpp/io/basic_ostream/operator_ltlt进一步挖掘,我注意到std::ostream::operator<<()没有char作为参数类型的重载.函数调用std::cout.operator<<(a)被解析为std::ostream::operator<<(int),这解释了输出.
我假设和operator<<之间的函数在其他地方声明为:std::ostreamchar
std::ostream& operator<<(std::ostream& out, char c);
Run Code Online (Sandbox Code Playgroud)
否则,std::cout << a将解决std::ostream::operator<<(int).
我的问题是为什么声明/定义为非成员函数?是否存在阻止其成为会员功能的已知问题?
我想知道为什么std::map并std::set使用std::less默认仿函数来比较键.为什么不使用类似于strcmp的仿函数?就像是:
template <typename T> struct compare
{
// Return less than 0 if lhs < rhs
// Return 0 if lhs == rhs
// Return greater than 0 if lhs > rhs
int operator()(T const& lhs, T const& rhs)
{
return (lhs-rhs);
}
}
Run Code Online (Sandbox Code Playgroud)
说一个map有两个对象,用键key1和key2.现在我们要插入另一个带键的对象key3.
使用时std::less,该insert功能需要先std::less::operator()用key1和调用key3.假设std::less::operator()(key1, key3)返回false.它必须std::less::operator()再次通过键切换std::less::operator()(key3, key1),以决定是否key1等于 …
我注意到伪析构函数调用在使用类型名称时有效,但在使用基本类型时则无效.
typedef int BType;
int b;
b.~BType(); // Legal
b.~int(); // Not legal
Run Code Online (Sandbox Code Playgroud)
可以在对另一个SO帖子的答案中找到对上述的解释.
类型名称的定义,来自C++ 11标准:
7.1.6.2简单类型说明符,p1
type-name: class-name enum-name typedef-name simple-template-id
当类型说明符是类型名称时是否有任何其他语言结构,但是当它是基本类型时无效,即使type-name表示基本类型,如上所示?
C++草案标准(N3337)有关于指针转换的以下内容:
4.10指针转换
2类型的右值"指针CV
T",其中T是对象类型,可被转化为式的一个右值"指针CVvoid".一个"指针转换的结果CVT"发送给"指针CVvoid"指向类型对象T所在的存储位置的起始位置,就好像该对象是类型最派生的对象(1.8)T(即不是基类子对象).
和
4.12布尔转换
1算术,枚举,指针或指向成员类型的指针的右值可以转换为类型的右值
bool.零值,空指针值或空成员指针值转换为false; 任何其他值都转换为true
基于以上所述,将函数指针或指针转换int为a void*也是完全可以的bool.
但是,考虑到两者的选择,指针应该转换为哪一个?
然后,为什么指向函数的指针转换为a bool和指向int转换为void*?的指针?
程序:
#include <iostream>
using namespace std;
void foo(const void* ptr)
{
std::cout << "In foo(void*)" << std::endl;
}
void foo(bool b)
{
std::cout << "In foo(bool)" << std::endl;
}
void bar()
{
}
int main()
{
int i …Run Code Online (Sandbox Code Playgroud) 在C++ 03标准中,我看到:
5.3.5删除
2如果操作数具有类类型,则通过调用上述转换函数将操作数转换为指针类型,并使用转换后的操作数代替本节其余部分的原始操作数.在任一替代方案中,如果操作数的值
delete是空指针,则操作无效.在第一个备选(删除对象)中,操作数的值delete应该是指向非数组对象的指针或指向表示此类对象的基类的子对象(1.8)的指针(第10节).如果不是,则行为未定义.在第二种方法(删除数组)中,操作数的delete值必须是由前一个数组new-expression产生的指针值.72)如果不是,则行为是未定义的.
在C++ 11 Draft Standard(N3337)中,我看到:
5.3.5删除
2如果操作数具有类类型,则通过调用上述转换函数将操作数转换为指针类型,并使用转换后的操作数代替本节其余部分的原始操作数.在第一个替代(删除对象)中,操作数的值
delete可以是空指针值,指向由前一个新表达式创建的非数组对象的指针,或指向表示基础的子对象(1.8)的指针这类对象的类别(第10条).如果不是,则行为未定义.在第二个备选(删除数组)中,delete的操作数的值可以是空指针值或由先前数组new-expression产生的指针值.如果不是,则行为未定义.
我已经强调了两个标准中规范之间的差异.我觉得奇怪的是,2003标准更加强调必须如何处理NULL指针,而2011标准没有说明实现必须做什么.
C++ 11标准的措辞是否在草案标准和实际标准之间发生了变化?如果是这样,怎么样?
如果标准草案的措辞在实际标准中保持不变,那么在2003年和2011年之间将强有力的陈述改为几乎没有任何理由的理由是什么?
以下代码编译得很好,我不知道为什么.有人可以向我解释为什么这是合法的吗?
我正在使用g ++(Debian 6.1.1-10)6.1.1 20160724进行编译.
#include <iostream>
int sum(int x, int y) { return x + y; }
int main(int argc, char *argv[])
{
using std::cout;
int (*) (int, int) = ∑
cout << "what" << '\n';
}
Run Code Online (Sandbox Code Playgroud)
附录
以下程序使用g ++版本5.4.0进行编译,但无法在gcc中编译.
int main()
{
int (*) = 20;
}
Run Code Online (Sandbox Code Playgroud) 我正在阅读__noop和MSDN示例
#if DEBUG
#define PRINT printf_s
#else
#define PRINT __noop
#endif
int main() {
PRINT("\nhello\n");
}
Run Code Online (Sandbox Code Playgroud)
我没有看到只有空宏的好处:
#define PRINT
Run Code Online (Sandbox Code Playgroud)
生成的代码是相同的.什么是使用__noop它的有效例子实际上使它有用?