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

Why is always_false_v required in this situation?

提问人:CakePlusPlus 提问时间:7/13/2023 更新时间:7/13/2023 访问量:251

问:

我正在用于指定项目中实体可能具有的属性类型,并偶然发现了 cpppreference 中的这段代码std::variant

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;

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

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

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

为什么还不够?我希望即使在编译时也永远不会执行,如果它一直执行,为什么不等价(看起来它肯定不可能)?falseelsealways_false_v<T>falsetrue

C++ C++17 IF-constExpr

评论


答:

6赞 Brian Bi 7/13/2023 #1

这里讨论这个问题。乔纳森·韦克利(Jonathan Wakely)的回答特别有用。

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

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

使整个模板格式不正确的事实特别令人讨厌,因为早期诊断实际上对程序员没有帮助。出于这个原因,C++23 中的规则已经更改,为静态断言提供了特殊的豁免:现在只有在实际实例化程序时才会使程序格式错误。static_assert(false)static_assert(false)

3赞 Remy Lebeau 7/13/2023 #2

static_assert(false, ...)总是被评估,所以总是会立即失败。您需要将第一个参数依赖于模板计算,以便仅当其他分支的计算结果为 false 时才对其进行评估。if

有关更深入的解释,请参阅Raymond Chen关于此主题的博客文章:

如何创建始终为 false 的类型相关表达式?

简而言之:

我必须做的一件事是,如果 lambda 调用不正确,则防止编译成功。我对有效案例进行了一系列测试,我需要在上面写着“你永远不应该到这里”上写一个。if constexprstatic_assertelse

...

但是,这不会编译,因为 [] 会立即失败。static_assert(false,...)

原因是 的控制表达式不依赖于参数的类型,因此在编译 lambda 时计算它,而不是在调用 lambda(和实例化隐式模板)时计算它。static_assert

为了推迟实例化,我们需要使其依赖于类型。static_assert