Qt项目中使用tl::expected调用私有构造函数编译失败问题
问题场景
在Qt项目(锁定C++17版本、禁用异常)中使用tl::expected 1.1.0时,遇到以下问题:
- 基础类型(如int)的
tl::expected可以正常初始化 - 尝试用Parent指针初始化
tl::expected<Child, std::string>时编译失败,报错无匹配构造函数,以及std::enable_if相关的"no type named 'type'"错误
代码示例
#include <include/tl/expected.hpp> class Parent { private: int n =42; }; class Child { public: Child() = delete; private: Parent* myParent; explicit Child(Parent *p = nullptr) : myParent { p } {}; }; int main() { // 正常编译 tl::expected<int, std::string> AnswerToUniverse{42}; Parent parent; Parent* ptr{ &parent }; // 编译失败 tl::expected<Child, std::string> child{ptr}; return 0; }
编译错误信息
/TLExpected/main.cpp:33: error: no matching function for call to ‘tl::expected<Child, std::__cxx11::basic_string
>::expected( )’ /TLExpected/build/Desktop-Debug/_deps/tl-expected-src/include/tl/expected.hpp:233:
error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
原因分析
Child的构造函数是私有的,tl::expected的构造逻辑无法访问这个私有构造函数来完成Child对象的构造。虽然传入的Parent*可以匹配Child的构造参数,但由于访问权限限制,SFINAE机制会过滤掉对应的构造重载,最终导致编译器找不到合适的expected构造函数,触发enable_if相关错误。
解决方案
我们需要让tl::expected能够合法访问Child的私有构造函数,或者通过Child提供的公共接口来创建tl::expected<Child, std::string>对象,以下是两种可行方案:
方案1:将tl::expected声明为Child的友元
修改Child类,添加友元声明,让tl::expected可以访问其私有构造函数:
class Child { public: Child() = delete; // 声明tl::expected为友元 template<typename T, typename E> friend class tl::expected; private: Parent* myParent; explicit Child(Parent *p = nullptr) : myParent { p } {}; };
修改后tl::expected<Child, std::string> child{ptr};可正常编译,因为expected的构造函数现在能直接调用Child的私有构造函数。
方案2:在Child中添加静态工厂函数
在Child类中添加公共静态函数,直接返回tl::expected<Child, std::string>对象,无需暴露私有构造函数的访问权限:
class Child { public: Child() = delete; // 静态工厂函数,创建并返回expected对象 static tl::expected<Child, std::string> create(Parent* p) { return tl::expected<Child, std::string>{tl::in_place, p}; } private: Parent* myParent; explicit Child(Parent *p = nullptr) : myParent { p } {}; };
在main函数中调用工厂函数创建对象:
// 替换原来的失败代码 auto child = Child::create(ptr);
这里使用tl::in_place直接在expected内部构造Child对象,避免额外拷贝。
内容的提问来源于stack exchange,提问作者ArchNemSyS

