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
相关产品推荐
相关产品推荐

