如何为分离实现的模板类应用enable_if_t模板约束?
模板类分离实现时使用enable_if_t导致编译错误
我习惯将模板类的声明和实现分离(声明放.h,实现放.hpp并在.h末尾包含),但给模板类添加enable_if基类约束后,分离实现的代码编译失败,内联实现则正常。
初始可运行代码
#include <iostream> #include <memory> #include <string> #define OddSource_Export __attribute((visibility("default"))) class IPAddress {}; class IPv4Address : public IPAddress { public: uint8_t version() const { return 4; } }; class IPv6Address : public IPAddress { public: uint8_t version() const { return 6; } }; template<class TAddress> class OddSource_Export InterfaceIPAddress { public: InterfaceIPAddress(TAddress const &, uint16_t); uint8_t version() const; private: ::std::unique_ptr<TAddress> const _address; uint16_t const _flags; }; template<class TAddress> InterfaceIPAddress<TAddress>:: InterfaceIPAddress(TAddress const &address, uint16_t flags) : _address(new TAddress(address)), _flags(flags) {} template<class TAddress> uint8_t InterfaceIPAddress<TAddress>:: version() const { return this->_address->version(); } int main() { IPv4Address addr4; InterfaceIPAddress<IPv4Address> address(addr4, 0); std::cout << "Version: " << std::to_string(address.version()) << std::endl; return 0; }
添加约束后的修改
为了限制模板参数必须继承自IPAddress,定义了以下别名:
template<class TAddress> using Enable_If_IPAddress = ::std::enable_if_t<::std::is_base_of_v<IPAddress, TAddress>>;
修改类声明为:
template<class TAddress, typename = Enable_If_IPAddress<TAddress>> class OddSource_Export InterfaceIPAddress
同时将构造函数和方法的模板头改为:
template<class TAddress, typename>
修改后的完整代码
#include <iostream> #include <memory> #include <string> #include <type_traits> #define OddSource_Export __attribute((visibility("default"))) class IPAddress {}; class IPv4Address : public IPAddress { public: uint8_t version() const { return 4; } }; class IPv6Address : public IPAddress { public: uint8_t version() const { return 6; } }; template<class TAddress> using Enable_If_IPAddress = ::std::enable_if_t<::std::is_base_of_v<IPAddress, TAddress>>; template<class TAddress, typename = Enable_If_IPAddress<TAddress>> class OddSource_Export InterfaceIPAddress { public: InterfaceIPAddress(TAddress const &, uint16_t); uint8_t version() const; private: ::std::unique_ptr<TAddress> const _address; uint16_t const _flags; }; template<class TAddress, typename> InterfaceIPAddress<TAddress>:: InterfaceIPAddress(TAddress const &address, uint16_t flags) : _address(new TAddress(address)), _flags(flags) {} template<class TAddress, typename> uint8_t InterfaceIPAddress<TAddress>:: version() const { return this->_address->version(); } int main() { IPv4Address addr4; InterfaceIPAddress<IPv4Address> address(addr4, 0); std::cout << "Version: " << std::to_string(address.version()) << std::endl; return 0; }
编译错误信息
Clang报错:
warning: missing 'typename' prior to dependent type name InterfaceIPAddress<TAddress>::InterfaceIPAddress; implicit 'typename' is a C++20 extension [-Wc++20-extensions] InterfaceIPAddress<TAddress>:: ^ typename error: expected ')' InterfaceIPAddress(TAddress const &address, uint16_t flags) ^ note: to match this '(' InterfaceIPAddress(TAddress const &address, uint16_t flags) ^ error: expected ';' at end of declaration : _address(new TAddress(address)), ^ ; error: no template named '_address'; did you mean 'TAddress'? : _address(new TAddress(address)), ^~~~~~~~ TAddress
GCC报错:
error: invalid use of incomplete type ‘class InterfaceIPAddress’ 38 | InterfaceIPAddress(TAddress const &address, uint16_t flags) | ^ main.cpp:26:24: note: declaration of ‘class InterfaceIPAddress’ 26 | class OddSource_Export InterfaceIPAddress | ^~~~~~~~~~~~~~~~~~
解决方案
问题出在类模板的完整参数列表匹配上:类模板有两个模板参数(TAddress和带默认值的第二个参数),但在实现成员函数时,你只指定了InterfaceIPAddress<TAddress>,编译器无法将其匹配到定义的双参数类模板,因此认为该类型未完成。
修正方法1:匹配完整模板参数
在成员函数实现时,明确指定完整的模板参数列表,包括第二个参数:
// 构造函数 template<class TAddress, typename Enable> InterfaceIPAddress<TAddress, Enable>::InterfaceIPAddress(TAddress const &address, uint16_t flags) : _address(new TAddress(address)), _flags(flags) {} // version方法 template<class TAddress, typename Enable> uint8_t InterfaceIPAddress<TAddress, Enable>::version() const { return this->_address->version(); }
修正方法2:使用C++20 requires约束(更简洁)
如果使用C++20及以上标准,推荐用requires约束替代enable_if,写法更直观,且分离实现时无需额外修改模板参数:
// 类声明 template<class TAddress> requires std::is_base_of_v<IPAddress, TAddress> class OddSource_Export InterfaceIPAddress { // 内部声明与初始代码一致 }; // 成员函数实现和最初的写法完全相同 template<class TAddress> InterfaceIPAddress<TAddress>::InterfaceIPAddress(TAddress const &address, uint16_t flags) : _address(new TAddress(address)), _flags(flags) {} template<class TAddress> uint8_t InterfaceIPAddress<TAddress>::version() const { return this->_address->version(); }
内容的提问来源于stack exchange,提问作者Nick Williams
相关产品推荐
相关产品推荐

