私有构造函数下std::optional的emplace调用编译失败问题问询
为什么std::optional::emplace无法访问类的private构造函数?
当类A的构造函数设为public时,makeA函数中使用return A(x, y)或o.emplace(x, y)均可编译通过;但将构造函数设为private后,o.emplace(x, y)无法编译,编译器提示类A不可构造。
示例代码
class A { public: // A(int x, int y) : x(x), y(y) {} // always compiles private: A(int x, int y) : x(x), y(y) {} // does not compile if using o.emplace(x, y) friend std::optional<A> makeA(int x, int y); int x, y; }; std::optional<A> makeA(int x, int y) { std::optional<A> o; if (x != 0 && y != 0) { return A(x, y); // o.emplace(x, y); } return o; }
编译错误信息
error: no matching function for call to ‘std::optional<A>::emplace(int&, int&)’ 70 | o.emplace(x, y); | ~~~~~~~~~^~~~~~ In file included from test.cpp:2: /usr/include/c++/11/optional:871:9: note: candidate: ‘template<class ... _Args> std::enable_if_t<is_constructible_v<_Tp, _Args ...>, _Tp&> std::optional<_Tp>::emplace(_Args&& ...) [with _Args = {_Args ...}; _Tp = A]’ 871 | emplace(_Args&&... __args) | ^~~~~~~ /usr/include/c++/11/optional:871:9: note: template argument deduction/substitution failed: In file included from /usr/include/c++/11/bits/move.h:57, from /usr/include/c++/11/bits/stl_pair.h:59, from /usr/include/c++/11/bits/stl_algobase.h:64, from /usr/include/c++/11/memory:63, from test.cpp:1: /usr/include/c++/11/type_traits: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = A&]’: /usr/include/c++/11/optional:871:2: required by substitution of ‘template<class ... _Args> std::enable_if_t<is_constructible_v<A, _Args ...>, A&> std::optional<A>::emplace<_Args ...>(_Args&& ...) [with _Args = {int&, int&}]’ test.cpp:70:14: required from here /usr/include/c++/11/type_traits:2579:11: error: no type named ‘type’ in ‘struct std::enable_if<false, A&>’ 2579 | using enable_if_t = typename enable_if<_Cond, _Tp>::type; | ^~~~~~~~~~~ In file included from test.cpp:2: /usr/include/c++/11/optional:883:9: note: candidate: ‘template<class _Up, class ... _Args> std::enable_if_t<is_constructible_v<_Tp, std::initializer_list<_Up>&, _Args ...>, _Tp&> std::optional<_Tp>::emplace(std::initializer_list<_Up>, _Args&& ...) [with _Up = _Up; _Args = {_Args ...}; _Tp = A]’ 883 | emplace(initializer_list<_Up> __il, _Args&&... __args) | ^~~~~~~ /usr/include/c++/11/optional:883:9: note: template argument deduction/substitution failed: test.cpp:70:14: note: mismatched types ‘std::initializer_list<_Tp>’ and ‘int’ 70 | o.emplace(x, y);
原因解析
核心问题出在友元权限的作用范围和模板类型检查的上下文:
makeA是类A的友元,因此在makeA内部直接调用A(x, y)构造对象是合法的。返回这个对象给std::optional<A>时,optional只需要执行移动/拷贝构造(而非直接构造A),这一步不需要访问A的私有构造函数。std::optional::emplace的实现依赖std::is_constructible_v<A, int&, int&>类型特性检查,这个检查是在std命名空间的模板代码中进行的——std::optional的成员函数不在A的友元列表里,无法看到A的私有构造函数,导致is_constructible_v返回false,触发enable_if的SFINAE规则,最终emplace函数被排除在候选列表之外,编译失败。
解决建议
如果想在私有构造的场景下使用emplace,可以尝试将std::optional<A>的emplace成员函数设为A的友元,但这会依赖具体标准库实现,通用性较差;更稳妥的方式是保持return A(x, y)的写法,直接构造A对象后返回给optional。
内容的提问来源于stack exchange,提问作者Konstante
相关产品推荐
相关产品推荐

