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

C++中带const参数的std::variant拷贝构造GCC编译报错疑问

问题描述

有一个尚未得到解答的优质问题,关于包含const成员的union:
其中一条建议是使用std::variant作为替代。实际上,const类型的支持是被明确要求的,参见P0086 - Variant设计评审,相关段落说明:

variant<int, const int> variant可以处理const类型:这类类型仅能通过variant构造函数和emplace()方法赋值。
因此我认为variant的拷贝构造函数也应当支持const类型参数。
但我尝试实现该方案的代码:

#include <string>
#include <variant>

using S = std::variant<const int, const std::string>;

int main() {
    S s(1);
    S u = s;

    S v("abc");
    S w = v;
}

在GCC中编译失败,返回非常长的错误信息,此处仅引用开头部分:

In file included from <source>:2:
/opt/compiler-explorer/gcc-trunk-20211024/include/c++/12.0.0/variant: In instantiation of 'constexpr std::__detail::__variant::_Variadic_union<_First, _Rest ...>::_Variadic_union(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = 1; _Args = {const int&}; _First = const std::__cxx11::basic_string<char>; _Rest = {}]':
/opt/compiler-explorer/gcc-trunk-20211024/include/c++/12.0.0/variant:409:4:   required from 'constexpr std::__detail::__variant::_Variadic_union<_First, _Rest ...>::_Variadic_union(std::in_place_index_t<_Np>, _Args&& ...) [with long unsigned int _Np = 2; _Args = {const int&}; _First = const int; _Rest = {const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >}]'
/opt/compiler-explorer/gcc-trunk-20211024/include/c++/12.0.0/bits/stl_construct.h:119:7:   required from 'constexpr void std::_Construct(_Tp*, _Args&& ...) [with _Tp = std::__detail::__variant::_Variadic_union<const int, const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >; _Args = {const std::in_place_index_t<2>&, const int&}]'

请问是我的代码存在错误,还是GCC本应接受这段代码?


解答

结论

你的代码完全符合C++标准要求,编译失败是旧版本GCC的标准库实现缺陷导致。

原因说明

  1. C++标准明确规定std::variant支持const类型作为备选值,仅限制不能对已构造完成的const类型备选值执行赋值操作,拷贝构造属于新对象初始化场景,不在限制范围内。
  2. 你测试所用的2021年GCC 12开发预览版中,std::variant的底层union实现逻辑存在问题:拷贝构造时错误尝试对const成员执行赋值操作,而非直接调用拷贝构造函数初始化底层成员,触发了const成员不可修改的编译错误。
  3. 该缺陷已经在GCC 12正式版及之后的版本中修复,升级到最新稳定版GCC即可正常编译运行这段代码。

旧版本兼容方案

如果暂时无法升级编译器,可以将备选类型声明为非const,仅在访问层增加const约束,实现等效的不可修改效果:

#include <string>
#include <variant>

using S = std::variant<int, std::string>;

// 对外仅暴露const访问接口,保证值不可修改
template<typename T>
const T& get(const S& var) {
    return std::get<T>(var);
}

int main() {
    S s(1);
    S u = s;

    S v("abc");
    S w = v;

    // 读取操作正常,无法通过返回值修改variant内部存储
    int int_val = get<int>(s);
    const std::string& str_val = get<std::string>(v);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 14:45:04