为什么在这种情况下需要always_false_v?

B.M*_*.M. 5 c++ c++17 if-constexpr

我用来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);
Run Code Online (Sandbox Code Playgroud)

always_false_v定义为



template<class>
inline constexpr bool always_false_v = false;
Run Code Online (Sandbox Code Playgroud)

我知道这会在编译时检查我是否正在处理变体中的所有类型,这非常酷且有用,但我很困惑为什么always_false_v<T>需要这样做。

如果我从 s 中删除一个分支if,Visual Studio 中的智能感知会立即设置红色波浪线,因为static_assert失败。

如果我替换always_false_v<T>false,智能感知不会抱怨,但当我尝试构建时静态断言会失败。

为什么仅仅false还不够?我希望else即使在编译时也不会执行,如果它一直执行,为什么不always_false_v<T>等于false(它看起来true肯定是不可能的)?

Bri*_*ian 7

这个问题就在这里讨论。乔纳森·韦克利的回答特别丰富。

简而言之,因为static_assert(false)使程序格式错误,如果您放置static_assert(false)在模板或语句的分支中if constexpr,则意味着该模板或if constexpr分支的每个可能的实例都会使程序格式错误。当编译器发现每个可能的实例化都格式错误的模板或if constexpr分支时,即使此类实例化从未发生,它也会拒绝该程序。

但是,当您具有 form 的构造时static_assert(always_false_v<T>);,并非所有可能的实例化都会使其格式错误。always_false_v确实可能有一些专业化。当然,在您的程序中,您没有这样的专门化,但要点是您可以引入一个专门化,并且它可以在引用它的模板的定义之后。因为static_assert(always_false<T>)不满足“不可能的实例化是格式良好的”标准,所以它避免了 的问题static_assert(false)

导致整个模板格式错误的事实static_assert(false)尤其令人烦恼,因为早期诊断实际上对程序员没有帮助。因此,C++23 中的规则已更改,为静态断言提供了特殊豁免:static_assert(false)现在仅在实际实例化程序时才会使程序格式错误。