带可变参数的ioPin类:结构体参数初始化编译错误求助
解决ioPin类聚合初始化ioPinParams时的编译错误
编译错误信息
error: converting to 'const ioPin' from initializer list would use explicit constructor 'ioPin::ioPin(Args ...) [with Args = {gpioPort, gpioMode, pinState}]' 11 | t::A, .mode = gpioMode::output, .state = pinState::high });
相关代码定义
头文件(ioPin.hh)
/*ioPin.hh*/ struct ioPinParams { gpioPort port{gpioPort::null}; gpioPin pin{gpioPin::null}; gpioMode mode{gpioMode::input}; gpioPUPD pupd{gpioPUPD::disabled}; gpioOutputType oType{gpioOutputType::pushPull}; gpioOutputSpeed oSpeed{gpioOutputSpeed::low}; pinState state{pinState::low}; }; class ioPin { private: bool init(); bool init(const GPIO_TypeDef *); bool init(const gpioPort &); bool init(const ioPinParams &); bool init(const gpioPin &); bool init(const gpioMode &); bool init(const gpioPUPD &); bool init(const gpioOutputType&); bool init(const gpioOutputSpeed&); bool init(const pinState &); public: explicit ioPin(); template<class ...Args> explicit ioPin(Args ...args); ~ioPin(); }; template<class ...Args> ioPin::ioPin(Args ...args) { init(); (init(args),...); }
实现文件(ioPin.cpp)
/*ioPin.cpp*/ #include "ioPin.hh" ioPin::ioPin() { init(); }
测试代码
ioPin a; /* <--- 该初始化正常 */ ioPin b(gpioMode::output, gpioPin::_5, pinState::high, gpioPUPD::disabled, GPIOA); /* <--- 该初始化正常 */ ioPin c(GPIOA, gpioPin::_5, gpioMode::output, pinState::high); /* <--- 该初始化正常 */ ioPin d(gpioMode::output, gpioPin::_5, pinState::high, gpioPort::A); /* <--- 该初始化正常 */ ioPin e( {.port = gpioPort::A, .mode = gpioMode::output, .state = pinState::high }); /* <--- 此处出现错误 */ int main(void) { while (1) { /* code */ } return 0; }
已尝试的方法及问题
- 添加模板特化:
错误依旧。template<> ioPin::ioPin<ioPinParams>(ioPinParams params) { } - 移除构造函数的
explicit修饰符:编译通过,但bool init(const ioPinParams &)从未被调用,因为编译器会把初始化列表拆成单个参数匹配可变参数模板构造函数,而非构造ioPinParams对象。 - 重载接收
const ioPinParams &的构造函数:出现调用歧义,初始化列表既可以匹配该构造函数,也能拆成多个参数匹配可变参数模板构造函数。
解决方案
方法1:为可变参数模板添加SFINAE约束,排除ioPinParams匹配场景
修改类定义,为可变参数模板构造函数添加约束,让它仅在参数无法构造ioPinParams时才参与重载决议,同时显式添加接收ioPinParams的构造函数:
#include <type_traits> class ioPin { // 保持原有私有成员不变 public: explicit ioPin(); // 添加约束:参数包无法构造ioPinParams时才启用该模板 template<class ...Args, typename = std::enable_if_t< !std::is_constructible_v<ioPinParams, Args...> >> explicit ioPin(Args ...args); // 显式声明接收ioPinParams的构造函数 explicit ioPin(const ioPinParams& params); ~ioPin(); }; // 实现接收ioPinParams的构造函数 ioPin::ioPin(const ioPinParams& params) { init(); init(params); } // 可变参数模板构造函数实现不变 template<class ...Args, typename> ioPin::ioPin(Args ...args) { init(); (init(args),...); }
这样编译器会优先匹配ioPin(const ioPinParams&)构造函数,确保init(const ioPinParams&)被正确调用,同时避免歧义。
方法2:显式构造ioPinParams对象
如果不想修改类定义,可在初始化时显式构造ioPinParams,让编译器直接匹配可变参数模板的单参数场景:
ioPin e( ioPinParams{.port = gpioPort::A, .mode = gpioMode::output, .state = pinState::high} );
内容的提问来源于stack exchange,提问作者Juan_David
相关产品推荐
相关产品推荐

