基于SFINAE的类构造器编译错误排查及修复咨询
问题描述
编译带有SFINAE构造器的类时,出现如下错误:
In template: no type named 'type' in 'std::enable_if<false, void *>'; 'enable_if' cannot be used to disable this declaration
相关代码如下:
#include <iostream> #include <type_traits> namespace infra { namespace io { struct IoDataFrameFactory {}; struct IoDataFrame {}; } } template<typename T1, typename T2> class IoDataFramePair : public infra::io::IoDataFrame { public: IoDataFramePair(std::istream &is, const infra::io::IoDataFrameFactory &factory, typename std::enable_if_t<(std::is_constructible_v<T1, std::istream &, const infra::io::IoDataFrameFactory &> && std::is_constructible_v<T2, std::istream &, const infra::io::IoDataFrameFactory &>), void *> = nullptr) : first(is, factory), second(is, factory) { checkSuffix(is); } IoDataFramePair(std::istream &is, const infra::io::IoDataFrameFactory &factory, typename std::enable_if_t<(!std::is_constructible_v<T1, std::istream &, const infra::io::IoDataFrameFactory &> && std::is_constructible_v<T2, std::istream &, const infra::io::IoDataFrameFactory &>), void *> = nullptr) : first(is), second(is, factory) { checkSuffix(is); } IoDataFramePair(std::istream &is, const infra::io::IoDataFrameFactory &factory, typename std::enable_if_t<(std::is_constructible_v<T1, std::istream &, const infra::io::IoDataFrameFactory &> && !std::is_constructible_v<T2, std::istream &, const infra::io::IoDataFrameFactory &>), void *> = nullptr) : first(is, factory), second(is) { checkSuffix(is); } IoDataFramePair(std::istream &is, const infra::io::IoDataFrameFactory &factory, typename std::enable_if_t<(!std::is_constructible_v<T1, std::istream &, const infra::io::IoDataFrameFactory &> && !std::is_constructible_v<T2, std::istream &, const infra::io::IoDataFrameFactory &>), void *> = nullptr) : first(is), second(is) { checkSuffix(is); } private: int m_version; void checkSuffix(std::istream &is) { // Implement checkSuffix logic } std::istream m_beginIS; std::istream m_middleIs; T1 first; T2 second; }; int main() { std::stringstream is(); infra::io::IoDataFrameFactory factory; IoDataFramePair<int, double> pair1(is, factory); IoDataFramePair<int, int> pair2(is, factory); return 0; }
问题原因
SFINAE(替换失败并非错误)仅在函数模板的参数替换阶段生效。当前代码中,构造器不是模板函数,其enable_if的条件依赖于类模板的参数T1、T2。当类被实例化时,四个构造器都会被完整检查:对于不符合条件的构造器,std::enable_if_t<false, void*>会直接触发编译错误(因为std::enable_if<false>没有type成员),而不会被SFINAE过滤掉。
修复方案
将构造器改为模板构造器,让enable_if的条件依赖于构造器自身的模板参数(通过默认参数绑定类的T1、T2),这样参数替换失败只会过滤掉当前构造器模板的实例,而不会导致整个类编译失败。
修复后的代码如下:
#include <iostream> #include <type_traits> #include <sstream> // 补充std::stringstream的头文件 namespace infra { namespace io { struct IoDataFrameFactory {}; struct IoDataFrame {}; } } template<typename T1, typename T2> class IoDataFramePair : public infra::io::IoDataFrame { public: // 将构造器改为模板,引入额外模板参数,默认值绑定类的T1、T2 template<typename U1 = T1, typename U2 = T2> IoDataFramePair(std::istream &is, const infra::io::IoDataFrameFactory &factory, typename std::enable_if_t<(std::is_constructible_v<U1, std::istream &, const infra::io::IoDataFrameFactory &> && std::is_constructible_v<U2, std::istream &, const infra::io::IoDataFrameFactory &>), void *> = nullptr) : m_beginIS(is), m_middleIs(is), first(is, factory), second(is, factory) { checkSuffix(is); } template<typename U1 = T1, typename U2 = T2> IoDataFramePair(std::istream &is, const infra::io::IoDataFrameFactory &factory, typename std::enable_if_t<(!std::is_constructible_v<U1, std::istream &, const infra::io::IoDataFrameFactory &> && std::is_constructible_v<U2, std::istream &, const infra::io::IoDataFrameFactory &>), void *> = nullptr) : m_beginIS(is), m_middleIs(is), first(is), second(is, factory) { checkSuffix(is); } template<typename U1 = T1, typename U2 = T2> IoDataFramePair(std::istream &is, const infra::io::IoDataFrameFactory &factory, typename std::enable_if_t<(std::is_constructible_v<U1, std::istream &, const infra::io::IoDataFrameFactory &> && !std::is_constructible_v<U2, std::istream &, const infra::io::IoDataFrameFactory &>), void *> = nullptr) : m_beginIS(is), m_middleIs(is), first(is, factory), second(is) { checkSuffix(is); } template<typename U1 = T1, typename U2 = T2> IoDataFramePair(std::istream &is, const infra::io::IoDataFrameFactory &factory, typename std::enable_if_t<(!std::is_constructible_v<U1, std::istream &, const infra::io::IoDataFrameFactory &> && !std::is_constructible_v<U2, std::istream &, const infra::io::IoDataFrameFactory &>), void *> = nullptr) : m_beginIS(is), m_middleIs(is), first(is), second(is) { checkSuffix(is); } private: int m_version; void checkSuffix(std::istream &is) { // Implement checkSuffix logic } // std::istream不可拷贝,改为引用类型 std::istream& m_beginIS; std::istream& m_middleIs; T1 first; T2 second; }; int main() { std::stringstream is; infra::io::IoDataFrameFactory factory; IoDataFramePair<int, double> pair1(is, factory); IoDataFramePair<int, int> pair2(is, factory); return 0; }
额外说明:
- 原代码中
std::istream m_beginIS;和std::istream m_middleIs;是值类型,而std::istream不可拷贝,会导致编译错误,修复时改为引用类型并在构造初始化列表中完成初始化。 - 补充了
#include <sstream>头文件,否则std::stringstream无法被识别。
内容的提问来源于stack exchange,提问作者Yehonatan
相关产品推荐
相关产品推荐

