You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

继承带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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:42:40