程序员的错误或 gcc-5.1.0 的错误?
Programmer's error or gcc-5.1.0 bug?
我尝试使用带有优化标志的 gcc-5.1.0 编译一个大型软件 -O1/-O2/-O3/-Og。它给了我 -Wmaybe-uninitialized
或 -Wuninitialized
警告并在运行时失败。调试后我找到了导致它的代码,但我无法理解为什么。我减少了重现失败的代码:
#include <cstdlib>
#include <iostream>
template<class T>
struct foo {
template<class U>
char bar(const U &x) {
//return id(x)[0];
const T &y = id(x);
return y[0];
}
const T &id(const T &elem) {
return elem;
}
};
int main(void) {
foo<const char *> f;
char *str = "hello world";
//std::cout << f.bar((const char *)str) << std::endl;
std::cout << f.bar(str) << std::endl;
return 0;
}
gcc-5.1.0 发出以下警告:
g++ -Og -Wall -Wextra -Wno-write-strings test.cpp -o test
test.cpp: In function ‘int main()’:
test.cpp:10:19: warning: ‘<anonymous>’ is used uninitialized in this function [-Wuninitialized]
return y[0];
^
test.cpp:9:24: note: ‘<anonymous>’ was declared here
const T &y = id(x);
^
程序在运行时收到 SIGSEGV
并崩溃。由于优化很难调试,但在玩过代码后我认为问题是 const T &y = id(x);
将 NULL
分配给 y
。 (通过将 return y[0];
替换为 returns y[0]
的函数调用来解决这个问题。)我目前无法使用其他版本的 gcc 测试代码,但是示例的代码是derived 在使用 gcc-4.9.2 编译时可以正常工作。当与 clang-3.6.1 一起编译时,它也可以在任何优化级别下正常工作。
我的下一步是试图找出导致它的确切优化是什么,所以我在我的 makefile 中放入了我能够找到的所有 gcc 优化标志,但它在没有警告的情况下编译并且在运行时不会崩溃。
我的问题是:
- 它是正确的 C++ 代码吗?
- 它是 gcc-5.1.0 中的 known/unknown 错误吗?
- 我是否理解正确,在上面的例子中
T = [const char *]; U = [char *]
和当我写 const T &y = id(x);
应该有 x
从 char *
到 [=26 的隐式转换=] 和 y
是对 const char *
? 的常量引用
- 如果模板有误,具体错误是什么?
- 如何找出导致它的确切优化标志是什么?
注意:取消注释任何注释行可以修复程序。
$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-unknown-linux-gnu/5.1.0/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: /build/gcc-multilib/src/gcc-5-20150623/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release --with-default-libstdcxx-abi=c++98
Thread model: posix
gcc version 5.1.0 (GCC)
我试过的 gcc 优化标志:
-falign-functions -falign-jumps -falign-labels -falign-loops -fassociative-math
-fauto-inc-dec -fbranch-count-reg -fbranch-probabilities -fbranch-target-load-optimize
-fbranch-target-load-optimize2 -fbtr-bb-exclusive -fcaller-saves -fcheck-data-deps -fcheck-new
-fcombine-stack-adjustments -fcompare-elim -fconserve-stack -fcprop-registers -fcrossjumping
-fcse-follow-jumps -fcse-skip-blocks -fcx-fortran-rules -fcx-limited-range -fdata-sections
-fdce -fdefer-pop -fdelete-null-pointer-checks -fdevirtualize -fdevirtualize-speculatively
-fdse -fearly-inlining -fexpensive-optimizations -fext-numeric-literals -ffast-math
-ffinite-math-only -ffloat-store -ffor-scope -fforward-propagate -ffriend-injection
-ffunction-sections -fgcse -fgcse-after-reload -fgcse-las -fgcse-lm -fgcse-sm -fguess-branch-probability
-fhoist-adjacent-loads -fif-conversion -fif-conversion2 -findirect-inlining -finline-functions
-finline-functions-called-once -finline-small-functions -fipa-cp -fipa-cp-alignment -fipa-cp-clone
-fipa-icf -fipa-matrix-reorg -fipa-profile -fipa-pta -fipa-pure-const -fipa-ra -fipa-reference
-fipa-sra -fipa-struct-reorg -fisolate-erroneous-paths-dereference -fivopts -fkeep-inline-functions
-fkeep-static-consts -floop-block -floop-interchange -floop-strip-mine -flra-remat -fmerge-all-constants
-fmerge-constants -fmodulo-sched -fmodulo-sched-allow-regmoves -fmove-loop-invariants -fms-extensions
-fnothrow-opt -fomit-frame-pointer -foptimize-register-move -foptimize-sibling-calls -foptimize-strlen
-fpartial-inlining -fpeel-loops -fpeephole2 -fpermissive -fpredictive-commoning -fprefetch-loop-arrays
-fprofile-correction -fprofile-generate -fprofile-use -fprofile-values -freciprocal-math -fregmove
-frename-registers -freorder-blocks -freorder-blocks-and-partition -freorder-functions -frerun-cse-after-loop
-freschedule-modulo-scheduled-loops -frounding-math -fsched-interblock -fsched-spec -fsched-spec-load
-fsched-spec-load-dangerous -fsched-stalled-insns -fsched-stalled-insns-dep -fsched2-use-superblocks
-fsched2-use-traces -fschedule-insns -fschedule-insns2 -fsee -fsel-sched-pipelining
-fsel-sched-pipelining-outer-loops -fselective-scheduling -fselective-scheduling2 -fshrink-wrap
-fsignaling-nans -fsingle-precision-constant -fsized-deallocation -fsplit-ivs-in-unroller
-fsplit-wide-types -fssa-phiopt -fstack-protector -fstack-protector-all -fstrict-aliasing
-fstrict-overflow -fthread-jumps -ftracer -ftree-bit-ccp -ftree-builtin-call-dce -ftree-ccp
-ftree-ch -ftree-coalesce-vars -ftree-copy-prop -ftree-copyrename -ftree-dce -ftree-dominator-opts
-ftree-dse -ftree-forwprop -ftree-fre -ftree-loop-distribute-patterns -ftree-loop-distribution
-ftree-loop-im -ftree-loop-ivcanon -ftree-loop-linear -ftree-loop-optimize -ftree-loop-vectorize
-ftree-partial-pre -ftree-phiprop -ftree-pre -ftree-pta -ftree-reassoc -ftree-sink -ftree-slp-vectorize
-ftree-slsr -ftree-sra -ftree-switch-conversion -ftree-tail-merge -ftree-ter -ftree-vect-loop-version
-ftree-vectorize -ftree-vrp -funit-at-a-time -funroll-all-loops -funroll-loops -funsafe-loop-optimizations
-funsafe-math-optimizations -funswitch-loops -fuse-cxa-atexit -fvariable-expansion-in-unroller
-fvect-cost-model -fvisibility-inlines-hidden -fvisibility-ms-compat -fvpt -fvtv-counts -fvtv-debug
-fweb -fwhole-program
您的字符串文字(非法)绑定到 char*
:
char *str = "hello world";
这是 C 的遗留转换,自 C++98(或 C++03?)以来已被弃用,并在 C++11 中被删除。 gcc 仍然允许它作为扩展,clang++ 也是如此。
当你调用f.bar(str)
时,它推导出U == char*
,所以它的函数参数类型变成了char* const&
。
using U = char*;
template<>
char bar(const U &x) { // char* const& x
const T &y = id(x);
return y[0];
}
但是,您已经使用模板参数 char const*
实例化了 f
,因此 id
的函数参数类型为 char const* const&
:
const T &id(const T &elem) { // char const* const& elem
return elem;
}
因此,bar
中的表达式 id(x)
必须从 char*
转换为 char const*
,这会创建一个临时变量。这个临时值一直持续到完整表达式 const T& y = id(x);
结束,因此创建了一个悬空引用 y
.
基本上,您的假设之一是不正确/不完整的:
Do I understand it correctly that in the example above T = [const char*]; U = [char *]
and when I write const T &y = id(x);
there should be implicit convertion of x
from char *
to const char *
and y
is a
constant reference to const char *
?
类型 T const*
和 T*
不是引用兼容的。有关详细信息,请参阅 [dcl.init.ref]。但是,T*
可以转换为 T const*
,因此创建一个临时文件并绑定到 T const*&
。这类似于:
int i = 42;
double const& d = i; // creates a temporary double and binds it to d
这是所有引用不兼容的类型的行为,其中源可以隐式转换为目标类型(除了 class 具有转换 functions/operators 的类型)。
为什么 T const*
和 T*
引用不兼容?同样的推理适用于 T const**
与 T**
,它们也是不兼容的。这可以防止出现细微错误,请参阅
我尝试使用带有优化标志的 gcc-5.1.0 编译一个大型软件 -O1/-O2/-O3/-Og。它给了我 -Wmaybe-uninitialized
或 -Wuninitialized
警告并在运行时失败。调试后我找到了导致它的代码,但我无法理解为什么。我减少了重现失败的代码:
#include <cstdlib>
#include <iostream>
template<class T>
struct foo {
template<class U>
char bar(const U &x) {
//return id(x)[0];
const T &y = id(x);
return y[0];
}
const T &id(const T &elem) {
return elem;
}
};
int main(void) {
foo<const char *> f;
char *str = "hello world";
//std::cout << f.bar((const char *)str) << std::endl;
std::cout << f.bar(str) << std::endl;
return 0;
}
gcc-5.1.0 发出以下警告:
g++ -Og -Wall -Wextra -Wno-write-strings test.cpp -o test
test.cpp: In function ‘int main()’:
test.cpp:10:19: warning: ‘<anonymous>’ is used uninitialized in this function [-Wuninitialized]
return y[0];
^
test.cpp:9:24: note: ‘<anonymous>’ was declared here
const T &y = id(x);
^
程序在运行时收到 SIGSEGV
并崩溃。由于优化很难调试,但在玩过代码后我认为问题是 const T &y = id(x);
将 NULL
分配给 y
。 (通过将 return y[0];
替换为 returns y[0]
的函数调用来解决这个问题。)我目前无法使用其他版本的 gcc 测试代码,但是示例的代码是derived 在使用 gcc-4.9.2 编译时可以正常工作。当与 clang-3.6.1 一起编译时,它也可以在任何优化级别下正常工作。
我的下一步是试图找出导致它的确切优化是什么,所以我在我的 makefile 中放入了我能够找到的所有 gcc 优化标志,但它在没有警告的情况下编译并且在运行时不会崩溃。
我的问题是:
- 它是正确的 C++ 代码吗?
- 它是 gcc-5.1.0 中的 known/unknown 错误吗?
- 我是否理解正确,在上面的例子中
T = [const char *]; U = [char *]
和当我写const T &y = id(x);
应该有x
从char *
到 [=26 的隐式转换=] 和y
是对const char *
? 的常量引用
- 如果模板有误,具体错误是什么?
- 如何找出导致它的确切优化标志是什么?
注意:取消注释任何注释行可以修复程序。
$ g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-unknown-linux-gnu/5.1.0/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: /build/gcc-multilib/src/gcc-5-20150623/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release --with-default-libstdcxx-abi=c++98
Thread model: posix
gcc version 5.1.0 (GCC)
我试过的 gcc 优化标志:
-falign-functions -falign-jumps -falign-labels -falign-loops -fassociative-math
-fauto-inc-dec -fbranch-count-reg -fbranch-probabilities -fbranch-target-load-optimize
-fbranch-target-load-optimize2 -fbtr-bb-exclusive -fcaller-saves -fcheck-data-deps -fcheck-new
-fcombine-stack-adjustments -fcompare-elim -fconserve-stack -fcprop-registers -fcrossjumping
-fcse-follow-jumps -fcse-skip-blocks -fcx-fortran-rules -fcx-limited-range -fdata-sections
-fdce -fdefer-pop -fdelete-null-pointer-checks -fdevirtualize -fdevirtualize-speculatively
-fdse -fearly-inlining -fexpensive-optimizations -fext-numeric-literals -ffast-math
-ffinite-math-only -ffloat-store -ffor-scope -fforward-propagate -ffriend-injection
-ffunction-sections -fgcse -fgcse-after-reload -fgcse-las -fgcse-lm -fgcse-sm -fguess-branch-probability
-fhoist-adjacent-loads -fif-conversion -fif-conversion2 -findirect-inlining -finline-functions
-finline-functions-called-once -finline-small-functions -fipa-cp -fipa-cp-alignment -fipa-cp-clone
-fipa-icf -fipa-matrix-reorg -fipa-profile -fipa-pta -fipa-pure-const -fipa-ra -fipa-reference
-fipa-sra -fipa-struct-reorg -fisolate-erroneous-paths-dereference -fivopts -fkeep-inline-functions
-fkeep-static-consts -floop-block -floop-interchange -floop-strip-mine -flra-remat -fmerge-all-constants
-fmerge-constants -fmodulo-sched -fmodulo-sched-allow-regmoves -fmove-loop-invariants -fms-extensions
-fnothrow-opt -fomit-frame-pointer -foptimize-register-move -foptimize-sibling-calls -foptimize-strlen
-fpartial-inlining -fpeel-loops -fpeephole2 -fpermissive -fpredictive-commoning -fprefetch-loop-arrays
-fprofile-correction -fprofile-generate -fprofile-use -fprofile-values -freciprocal-math -fregmove
-frename-registers -freorder-blocks -freorder-blocks-and-partition -freorder-functions -frerun-cse-after-loop
-freschedule-modulo-scheduled-loops -frounding-math -fsched-interblock -fsched-spec -fsched-spec-load
-fsched-spec-load-dangerous -fsched-stalled-insns -fsched-stalled-insns-dep -fsched2-use-superblocks
-fsched2-use-traces -fschedule-insns -fschedule-insns2 -fsee -fsel-sched-pipelining
-fsel-sched-pipelining-outer-loops -fselective-scheduling -fselective-scheduling2 -fshrink-wrap
-fsignaling-nans -fsingle-precision-constant -fsized-deallocation -fsplit-ivs-in-unroller
-fsplit-wide-types -fssa-phiopt -fstack-protector -fstack-protector-all -fstrict-aliasing
-fstrict-overflow -fthread-jumps -ftracer -ftree-bit-ccp -ftree-builtin-call-dce -ftree-ccp
-ftree-ch -ftree-coalesce-vars -ftree-copy-prop -ftree-copyrename -ftree-dce -ftree-dominator-opts
-ftree-dse -ftree-forwprop -ftree-fre -ftree-loop-distribute-patterns -ftree-loop-distribution
-ftree-loop-im -ftree-loop-ivcanon -ftree-loop-linear -ftree-loop-optimize -ftree-loop-vectorize
-ftree-partial-pre -ftree-phiprop -ftree-pre -ftree-pta -ftree-reassoc -ftree-sink -ftree-slp-vectorize
-ftree-slsr -ftree-sra -ftree-switch-conversion -ftree-tail-merge -ftree-ter -ftree-vect-loop-version
-ftree-vectorize -ftree-vrp -funit-at-a-time -funroll-all-loops -funroll-loops -funsafe-loop-optimizations
-funsafe-math-optimizations -funswitch-loops -fuse-cxa-atexit -fvariable-expansion-in-unroller
-fvect-cost-model -fvisibility-inlines-hidden -fvisibility-ms-compat -fvpt -fvtv-counts -fvtv-debug
-fweb -fwhole-program
您的字符串文字(非法)绑定到 char*
:
char *str = "hello world";
这是 C 的遗留转换,自 C++98(或 C++03?)以来已被弃用,并在 C++11 中被删除。 gcc 仍然允许它作为扩展,clang++ 也是如此。
当你调用f.bar(str)
时,它推导出U == char*
,所以它的函数参数类型变成了char* const&
。
using U = char*;
template<>
char bar(const U &x) { // char* const& x
const T &y = id(x);
return y[0];
}
但是,您已经使用模板参数 char const*
实例化了 f
,因此 id
的函数参数类型为 char const* const&
:
const T &id(const T &elem) { // char const* const& elem
return elem;
}
因此,bar
中的表达式 id(x)
必须从 char*
转换为 char const*
,这会创建一个临时变量。这个临时值一直持续到完整表达式 const T& y = id(x);
结束,因此创建了一个悬空引用 y
.
基本上,您的假设之一是不正确/不完整的:
Do I understand it correctly that in the example above
T = [const char*]; U = [char *]
and when I writeconst T &y = id(x);
there should be implicit convertion ofx
fromchar *
toconst char *
andy
is a constant reference toconst char *
?
类型 T const*
和 T*
不是引用兼容的。有关详细信息,请参阅 [dcl.init.ref]。但是,T*
可以转换为 T const*
,因此创建一个临时文件并绑定到 T const*&
。这类似于:
int i = 42;
double const& d = i; // creates a temporary double and binds it to d
这是所有引用不兼容的类型的行为,其中源可以隐式转换为目标类型(除了 class 具有转换 functions/operators 的类型)。
为什么 T const*
和 T*
引用不兼容?同样的推理适用于 T const**
与 T**
,它们也是不兼容的。这可以防止出现细微错误,请参阅