为何在std::variant访问场景下必须使用always_false_v?
我在项目中用std::variant定义实体属性类型时,看到cppreference上的这段代码:
std::visit([](auto&& arg) { using T = std::decay_t<decltype(arg)>; if constexpr (std::is_same_v<T, int>) std::cout << "int with value " << arg << '\n'; else if constexpr (std::is_same_v<T, long>) std::cout << "long with value " << arg << '\n'; else if constexpr (std::is_same_v<T, double>) std::cout << "double with value " << arg << '\n'; else if constexpr (std::is_same_v<T, std::string>) std::cout << "std::string with value " << std::quoted(arg) << '\n'; else static_assert(always_false_v<T>, "non-exhaustive visitor!"); }, w);
always_false_v的定义如下:
template<class> inline constexpr bool always_false_v = false;
我明白这是用来在编译时检查是否处理了std::variant的所有类型,但困惑为何必须使用always_false_v<T>而非直接用false。移除某个if分支时,Visual Studio智能感知会立刻报错,但替换为false后智能感知无提示却编译失败,想知道其中原因。
这本质是C++编译期常量表达式的依赖性规则导致的:
直接使用
false的问题
写static_assert(false, ...)时,断言条件是非依赖常量表达式——不依赖任何模板参数。根据C++标准,只要这段代码所在的模板被实例化(哪怕else分支永远不会执行),编译器就会立即检查这个断言,因为它是一个必然为假的常量表达式,会直接触发编译错误,哪怕所有if constexpr分支都覆盖了variant的所有类型。always_false_v<T>的核心作用always_false_v<T>是依赖模板参数T的常量表达式。只有当模板被实例化(即确实存在未处理的variant类型,走到了else分支)时,编译器才会计算这个值——此时T是具体的未处理类型,always_false_v<T>会被推导为false,触发断言报错。如果所有类型都被分支覆盖,else分支不会被实例化,这个static_assert根本不会被编译器检查,也就不会产生错误。VS智能感知的差异原因
VS智能感知对依赖型常量表达式的分析更精准:使用always_false_v<T>时,它能追踪到T是variant的类型参数,一旦有类型未被分支覆盖,就能立刻识别出else分支会被实例化,从而提前报错。而直接写false时,智能感知会将其视为无条件的错误断言,无法关联到variant的类型覆盖情况,所以无法提前给出提示,只有到编译阶段才会触发错误。
内容的提问来源于stack exchange,提问作者CakePlusPlus

