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

Qt项目中使用tl::expected调用私有构造函数编译失败问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:47:05