继承带printf格式属性的可变参数构造函数后GCC编译失败的解决方法
问题描述
以下代码可在Clang中编译通过,但无法在GCC中编译:
#include <cstdarg> #include <iostream> struct Base { Base(const char* fmt, ...) __attribute__((format(printf, 2, 3))) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } }; struct Derived : public Base { using Base::Base; }; int main() { Derived d("%d", 3); return 0; }
GCC报错信息:
<source>: In function 'int main()': <source>:18:20: sorry, unimplemented: passing arguments to ellipsis of inherited constructor 'Derived::Derived(const char*, ...) [inherited from Base]' 18 | Derived d("%d", 3); | ^ <source>:14:15: note: declared here 14 | using Base::Base; | ^~~~ Compiler returned: 1
实际需求:
- 修改代码使其在GCC正常运行
- 基类继承自
std::exception,需通过printf格式构造错误消息并传递给异常构造函数 - 基类和派生类均需具备编译期printf格式字符串验证能力
- 避免直接复制基类构造函数的实现逻辑
解决方案
方案1:显式编写带格式验证的转发构造函数
在派生类中显式声明转发构造函数,并添加与基类相同的__attribute__((format))属性,绕开GCC对继承可变参数构造函数的实现限制,同时保留编译期格式检查:
#include <cstdarg> #include <iostream> #include <stdexcept> struct Base : public std::exception { Base(const char* fmt, ...) __attribute__((format(printf, 2, 3))) { va_list ap; va_start(ap, fmt); // 实际场景中可使用vsnprintf构造异常消息并存储 vprintf(fmt, ap); va_end(ap); } }; struct Derived : public Base { Derived(const char* fmt, ...) __attribute__((format(printf, 2, 3))) : Base(fmt, __VA_ARGS__) {} }; int main() { Derived d("%d", 3); return 0; }
该方案改动小,完全保留格式验证能力,且无需复制基类的可变参数处理逻辑。
方案2:可变参数模板转发(C++11及以上)
借助可变参数模板实现通用转发,同时添加格式验证属性,兼容性更强:
#include <cstdarg> #include <iostream> #include <stdexcept> #include <utility> struct Base : public std::exception { Base(const char* fmt, ...) __attribute__((format(printf, 2, 3))) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } }; struct Derived : public Base { template <typename... Args> Derived(const char* fmt, Args&&... args) __attribute__((format(printf, 2, 3))) : Base(fmt, std::forward<Args>(args)...) {} }; int main() { Derived d("%d", 3); return 0; }
此方案同样保留编译期格式检查,且对基类后续的参数扩展兼容性更好。
方案3:封装格式处理逻辑为辅助函数
若基类的格式处理逻辑复杂,可将核心逻辑封装为私有静态函数,让基类和派生类的构造函数调用该函数,避免代码重复:
#include <cstdarg> #include <iostream> #include <stdexcept> #include <cstdio> struct Base : public std::exception { private: static void format_message(const char* fmt, va_list ap) { // 实际场景中构造并存储异常消息,例如: // char buf[256]; // vsnprintf(buf, sizeof(buf), fmt, ap); // msg = buf; vprintf(fmt, ap); } protected: Base(const char* fmt, va_list ap) { format_message(fmt, ap); } public: Base(const char* fmt, ...) __attribute__((format(printf, 2, 3))) { va_list ap; va_start(ap, fmt); format_message(fmt, ap); va_end(ap); } }; struct Derived : public Base { Derived(const char* fmt, ...) __attribute__((format(printf, 2, 3))) { va_list ap; va_start(ap, fmt); Base(fmt, ap); va_end(ap); } }; int main() { Derived d("%d", 3); return 0; }
该方案将格式处理逻辑集中管理,基类和派生类仅负责参数转发,既避免代码冗余,又保留格式验证能力。
内容的提问来源于stack exchange,提问作者dshin
相关产品推荐
相关产品推荐

