使用avr-g++ 5.4.0编译C++17代码时if constexpr报错的原因排查
问题分析:avr-g++ 5.4.0编译C++17代码失败
问题背景
我在编写的类方法中定义了如下FixedPointFraction结构体:
template <typename T = int_fast16_t, uint8_t Bits = sizeof(T) * 8 - 2> struct FixedPointFraction { using value_type = T; constexpr static uint8_t kFractionBits = Bits; // ... template <typename U, uint8_t UBits = sizeof(U) * 2 - 2> FixedPointFraction<U, UBits> Convert() const { using Target = FixedPointFraction<U, UBits>; static constexpr int kShift = Target::kFractionBits - kFractionBits; // Fails to compile `if constexpr` below: if constexpr (kShift > 0) { return Target(static_cast<U>(fraction_bits << kShift)); } else if (kShift < 0) { return Target{fraction_bits >> kShift}; } else { return Target{fraction_bits}; } } // ... };
使用avr-g++ (GCC) 5.4.0编译时失败,编译命令:
avr-g++ -g -DF_CPU=3'333'333L -std=c++17 -DNDEBUG -fdata-sections \ -ffunction-sections -fno-exceptions -flto=auto -Wall -Os -Werror -Wextra \ -B build/atpack/gcc/dev/attiny3224 -isystem build/atpack/include \ -mmcu=attiny3224 -Wa,-ahlmns=build/timer.lst -c -o build/timer.o timer.cc
错误信息:
util.h: In member function ...: util.h:105:8: error: expected ‘(’ before ‘constexpr’ if constexpr (kShift > 0) { ^
avr-g++版本信息:
% avr-g++ -v Using built-in specs. Reading specs from /usr/lib/gcc/avr/5.4.0/device-specs/specs-avr2 COLLECT_GCC=avr-g++ COLLECT_LTO_WRAPPER=/usr/lib/gcc/avr/5.4.0/lto-wrapper Target: avr Configured with: ../gcc/configure -v --enable-languages=c,c++ --prefix=/usr/lib --infodir=/usr/share/info --mandir=/usr/share/man --bindir=/usr/bin --libexecdir=/usr/lib --libdir=/usr/lib --enable-shared --with-system-zlib --enable-long-long --enable-nls --without-included-gettext --disable-libssp --build=aarch64-linux-gnu --host=aarch64-linux-gnu --target=avr CFLAGS='-g -O2 -fdebug-prefix-map=/build/gcc-avr-mRLtUO/gcc-avr-5.4.0+Atmel3.6.2=. -fstack-protector-strong -Wformat ' CPPFLAGS='-Wdate-time -D_FORTIFY_SOURCE=2' CXXFLAGS='-g -O2 -fdebug-prefix-map=/build/gcc-avr-mRLtUO/gcc-avr-5.4.0+Atmel3.6.2=. -fstack-protector-strong -Wformat ' FCFLAGS='-g -O2 -fdebug-prefix-map=/build/gcc-avr-mRLtUO/gcc-avr-5.4.0+Atmel3.6.2=. -fstack-protector-strong' FFLAGS='-g -O2 -fdebug-prefix-map=/build/gcc-avr-mRLtUO/gcc-avr-5.4.0+Atmel3.6.2=. -fstack-protector-strong' GCJFLAGS='-g -O2 -fdebug-prefix-map=/build/gcc-avr-mRLtUO/gcc-avr-5.4.0+Atmel3.6.2=. -fstack-protector-strong' LDFLAGS=-Wl,-z,relro OBJCFLAGS='-g -O2 -fdebug-prefix-map=/build/gcc-avr-mRLtUO/gcc-avr-5.4.0+Atmel3.6.2=. -fstack-protector-strong -Wformat ' OBJCXXFLAGS='-g -O2 -fdebug-prefix-map=/build/gcc-avr-mRLtUO/gcc-avr-5.4.0+Atmel3.6.2=. -fstack-protector-strong -Wformat ' Thread model: single gcc version 5.4.0 (GCC)
原因分析
你的代码本身没有问题,编译失败是avr-g++ 5.4.0对C++17的支持不完整导致的:
if constexpr是C17引入的核心编译期分支特性,但GCC主分支直到7.x版本才完整实现该特性,5.x版本仅支持非常有限的C17预览特性。- avr平台的GCC版本更新滞后,5.4.0版本完全没有实现
if constexpr,编译器无法识别该语法,因此抛出语法错误。
解决方案
方案1:升级avr-g++版本
将avr-g升级到7.0及以上版本(推荐使用官方提供的最新avr-gcc工具链),新版本的avr-g对C++17特性的支持更完善,能够正确编译if constexpr代码。
方案2:替换if constexpr为C11/C14兼容的编译期分支
如果无法升级编译器,可以用编译期常量表达式判断替代if constexpr,实现同样的逻辑:
template <typename T = int_fast16_t, uint8_t Bits = sizeof(T) * 8 - 2> struct FixedPointFraction { using value_type = T; constexpr static uint8_t kFractionBits = Bits; T fraction_bits; // 假设结构体包含该成员变量 // ... template <typename U, uint8_t UBits = sizeof(U) * 2 - 2> FixedPointFraction<U, UBits> Convert() const { using Target = FixedPointFraction<U, UBits>; constexpr int kShift = Target::kFractionBits - kFractionBits; return Target{static_cast<U>( kShift == 0 ? fraction_bits : kShift > 0 ? (fraction_bits << kShift) : (fraction_bits >> (-kShift)) )}; } // ... };
avr-g++ 5.4.0支持C++11的常量表达式规则,编译器会在编译期完成分支判断,生成对应逻辑的代码。
内容的提问来源于stack exchange,提问作者Petr
相关产品推荐
相关产品推荐

