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

C++含unique_ptr参数的私有构造函数使用make_optional报错问题

私有构造函数结合std::optional的编译错误分析与解决

问题背景

我参考一篇文章实现了一个带有私有构造函数和静态“实例创建”函数的类,该函数根据条件返回std::optional对象:满足条件时调用构造函数创建实例,否则返回nullopt。但在实现过程中遇到了编译错误,以下是具体案例:


错误案例1:Engine1(含unique_ptr参数的私有构造函数)

编译报错信息:

error: no matching function for call to 'make_optional'
            return make_optional<Engine1>({move(p)});
                   ^~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/optional:1448:5: note: candidate function template not viable: cannot convert initializer list argument to 'Engine1'
    make_optional(_Tp&& __t)
    ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/optional:1456:5: note: candidate template ignored: substitution failure [with _Tp = Engine1]: deduced incomplete pack <(no value)> for template parameter '_Args'
    make_optional(_Args&&... __args)
    ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/optional:1464:5: note: candidate template ignored: requirement 'is_constructible_v<Engine1, std::initializer_list<std::unique_ptr<int, std::default_delete<int>>> &>' was not satisfied [with _Tp = Engine1, _Up = typename std::remove_reference<unique_ptr<int> &>::type, _Args = <>]
    make_optional(initializer_list<_Up> __il, _Args&&... __args)
    ^

对应的Engine1代码:

#include <iostream>
#include <optional>
#include <memory>
using namespace std;

class Engine1
{
private:
    explicit Engine1(unique_ptr<int> p): ptr(move(p)) {};
    unique_ptr<int> ptr;
public:
    static optional<Engine1> makeInstance()
    {
        auto p = make_unique<int>(123);
        bool success = true;

        if (success)
            return make_optional<Engine1>({move(p)});
        else
            return {};
    }
};

int main()
{
    auto ins = Engine1::makeInstance();
    return 0;
}

错误案例2:Engine2(含裸指针参数的私有构造函数)

编译报错信息:

error: no matching function for call to 'make_optional'
            return make_optional<Engine2>(p);
                   ^~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/optional:1448:5: note: candidate function template not viable: no known conversion from 'int *' to 'Engine2' for 1st argument
    make_optional(_Tp&& __t)
    ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/optional:1456:5: note: candidate template ignored: requirement 'is_constructible_v<Engine2, int *&>' was not satisfied [with _Tp = Engine2, _Args = <int *&>]
    make_optional(_Args&&... __args)
    ^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/optional:1464:5: note: candidate template ignored: could not match 'initializer_list<_Up>' against 'int *'
    make_optional(initializer_list<_Up> __il, _Args&&... __args)
    ^

对应的Engine2代码:

class Engine2
{
private:
    explicit Engine2(int *p): ptr(p) {};
    int *ptr;
public:
    static optional<Engine2> makeInstance()
    {
        auto p = new int(123);
        bool success = true;

        if (success)
            return make_optional<Engine2>(p);
        else
            return {};
    }
};

正常案例:Engine3(构造函数改为public)

修改构造函数为public后,代码可以正常编译:

class Engine3
{
private:
    int *ptr;
public:
    explicit Engine3(int *p): ptr(p) {};
    static optional<Engine3> makeInstance()
    {
        auto p = new int(123);
        bool success = true;

        if (success)
            return make_optional<Engine3>(p);
        else
            return {};
    }
};

但这种修复方式对unique_ptr的场景不适用,因此需要深入分析问题原因。


问题解答

1. Engine1和Engine2存在什么问题?

核心原因是**std::make_optional无法访问类的私有构造函数**。
std::make_optional是标准库提供的全局模板函数,它不属于目标类的友元,因此在尝试推导模板参数并检查is_constructible_v时,编译器会判定外部函数无法调用私有构造函数,导致所有重载的make_optional都无法匹配,最终触发编译错误。

2. make_optional对unique_ptr和裸指针的处理为何及如何不同?

两者的核心问题完全一致,都是私有构造函数的访问权限导致的is_constructible_v检查失败。报错信息的差异仅源于参数传递方式不同:

  • Engine1使用了初始化列表{move(p)},编译器优先尝试匹配make_optional的初始化列表重载,但由于无法调用私有构造函数,is_constructible_v<Engine1, std::initializer_list<std::unique_ptr<int>>>条件不成立,重载被排除。
  • Engine2直接传递裸指针p,编译器尝试匹配可变参数重载make_optional(_Args&&...),但is_constructible_v<Engine2, int*&>条件不成立(构造函数私有),重载同样被排除。

指针类型本身并没有影响make_optional的处理逻辑,只是错误提示的细节不同。


完整实现示例

要解决这个问题,有两种可靠的方式:

方式1:将std::make_optional声明为类的友元

允许make_optional访问私有构造函数:

#include <iostream>
#include <optional>
#include <memory>
using namespace std;

class Engine
{
private:
    explicit Engine(unique_ptr<int> p): ptr(move(p)) {};
    unique_ptr<int> ptr;

    // 声明make_optional模板为友元
    template<typename T, typename... Args>
    friend optional<T> make_optional(Args&&... args);

public:
    static optional<Engine> makeInstance()
    {
        auto p = make_unique<int>(123);
        bool success = true;

        if (success)
            return make_optional<Engine>(move(p));
        else
            return nullopt;
    }

    // 可选:添加成员函数验证实例是否有效
    void printValue() const {
        if (ptr) cout << *ptr << endl;
    }
};

int main()
{
    auto ins = Engine::makeInstance();
    if (ins) {
        ins->printValue();
    }
    return 0;
}

方式2:使用std::in_place直接构造optional

不需要依赖make_optional,直接在optional内部构造实例:

#include <iostream>
#include <optional>
#include <memory>
using namespace std;

class Engine
{
private:
    explicit Engine(unique_ptr<int> p): ptr(move(p)) {};
    unique_ptr<int> ptr;

public:
    static optional<Engine> makeInstance()
    {
        auto p = make_unique<int>(123);
        bool success = true;

        if (success)
            return optional<Engine>(in_place, move(p)); // 使用in_place直接构造
        else
            return nullopt;
    }

    void printValue() const {
        if (ptr) cout << *ptr << endl;
    }
};

int main()
{
    auto ins = Engine::makeInstance();
    if (ins) {
        ins->printValue();
    }
    return 0;
}

内容的提问来源于stack exchange,提问作者user8822312

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 03:10:03