在以下示例中,注意A::operator B()
不会返回值。
#include <stdio.h>
using namespace std;
struct B
{
int valb;
B(int x = 10) : valb(x) {
printf("\nB::B()...\n");
}
B(const B& rhs) : valb(rhs.valb) {
printf("\nB::B(const B&)...\n");
}
B(B& rhs) : valb(rhs.valb) {
printf("\nB::B(B&)...\n");
}
void f() const {
printf("\nB::f() const... [%d | 0x%x]\n", valb, this);
}
~B() {
printf("\nB::~B()...\n");
}
};
struct A {
static int gv;
int *vala;
A(int *x = &gv) : vala(x) {
printf("\nA::A()...\n");
}
A(const A& rhs) : vala(rhs.vala) {
printf("\nA::A(const A&)...\n");
}
void f() const {
printf("\nconst A::f()...\n");
}
operator B () {
printf("\nA::operator B()... [0x%x]\n", this);
// return B();
}
};
void func(const B& barg) {
printf("\nfunc(const B&)...\n");
barg.f();
}
int A::gv;
int main ()
{
A a1;
func(a1);
return 0;
}
使用g ++ 4.5.2(-std = gnu ++ 0x -fno-elide-constructors)在rhel5中构建时,会生成以下输出:
A::A()...
A::operator B()... [0x1cc30200]
func(const B&)...
B::f() const... [4196240 | 0x1cc30210]
B::~B()...
注意显示的2个不同地址表示正在创建2个不同的对象。
问题:
为什么没有调用任何B
构造函数?
在func()
内,B对象如何被调用B::f()
?
g ++不应该生成编译错误吗?
标准的第12.3.2节中是否有任何提示会使上述输出有效/预期?
答案 0 :(得分:5)
首先,它是未定义的行为,因为转换运算符没有返回任何内容。
为什么没有调用任何B构造函数?
因为程序中没有创建B对象。
在func()中,B对象在调用B :: f()时是如何创建的?
它未创建。程序具有未定义的行为,假设引用引用B
对象,但该内存位置没有B
对象(由于未能在operator B
中创建它
g ++不应该生成编译错误吗?
如果警告足够高,可能会有一些优化级别,您会收到警告。该标准不需要诊断,因此这只是实施的质量。
标准的第12.3.2节中是否有任何提示会使上述输出有效/预期?
没有什么可以使期望,但它显然是有效的标准,因为你的程序导致未定义的行为,无论编译器做什么,你看到发生的是一个有效的未定义的化身行为。
答案 1 :(得分:4)
在没有void
语句的情况下,非main()
函数(return
除外)的结尾是未定义的行为。无论发生什么,都是你得到的。如果你很幸运,你会崩溃。
它不是一个错误的原因是控制流可能永远不会真正地从函数的末尾掉落,但编译器不一定能看到它,例如。
#include <stdexcept>
int f(int i) {
if (i == 0) {
throw std::runtime_error("index out of range");
}
return 17;
}
int g() {
f(0);
// compile-time error would be invalid
}
int main() {
try { g(); }
catch (std::exception const&) { }
}
在上面的代码中,编译器可以检测f()
在使用0
调用时永远不会返回,但如果f()
来自某个库,则通常无法预测。
答案 2 :(得分:3)
您在转换运算符中有未定义的行为。任何事情都可能发生或不发生。在这个特殊情况下没有发生的事情是建造一个B。
这是一个等效的程序(对于少于55个参数的普通调用):
int main( int, char* argv[] ) { return *argv[55]; }