为何std::optional<std::unique_ptr<int>>不能作为constexpr函数返回类型?
为何该函数无法声明为constexpr?
首先看目标代码:
constexpr std::optional<std::unique_ptr<int>> test2(void) { return std::nullopt; }
Clang 18(C++20标准)给出的错误如下:
[build] /.../<>/.cxx:223:47: error: constexpr function's return type 'std::optional<std::unique_ptr<int>>' is not a literal type [build] 223 | constexpr std::optional<std::unique_ptr<int>> test2(void) { [build] | ^ [build] /nix/store/fsb7lmhyy01flrnviwjfz3fgm53w990v-libcxx-18.1.7-dev/include/c++/v1/optional:582:7: note: 'optional<std::unique_ptr<int>>' is not literal because it has base class '__optional_move_assign_base<unique_ptr<int, default_delete<int>>>' of non-literal type [build] 582 | : private __optional_move_assign_base<_Tp>,
原因分析
- constexpr函数的返回类型必须是字面类型(literal type),这是C++标准的硬性要求。
std::optional<std::unique_ptr<int>>不满足字面类型条件:Libcxx的std::optional实现继承了私有基类__optional_move_assign_base<_Tp>,而该基类的类型由std::unique_ptr<int>决定。- 在C++20中,
std::unique_ptr虽然支持部分constexpr操作,但它的析构函数不是constexpr,因此不属于字面类型。这导致包含它的基类也不是字面类型,最终std::optional实例也无法成为字面类型。
解决方法
升级到C23标准:C23将std::unique_ptr的析构函数标记为constexpr,此时std::unique_ptr成为字面类型,对应的std::optional也会满足字面类型要求,该函数即可正常编译。编译时添加选项-std=c++23即可。
可复现环境配置(Nix)
{ pkgs ? import <nixpkgs> {} }: pkgs.mkShell { nativeBuildInputs = with pkgs.buildPackages; [ # system tools zsh git openssl # basic configuration pkgs.llvmPackages_18.stdenv pkgs.llvmPackages_18.libcxx pkgs.llvmPackages_18.libcxxStdenv pkgs.llvmPackages_18.bintools pkgs.llvmPackages_18.clang-tools cmake # ... ]; }
注:将int替换为自定义类/结构体时,错误依然存在,本质原因还是std::unique_ptr的非字面类型属性导致。
内容的提问来源于stack exchange,提问作者user26082724
相关产品推荐
相关产品推荐

