gpt4 book ai didi

c++ - 范围和分解的原因不允许constexpr

转载 作者:行者123 更新时间:2023-11-30 02:22:18 25 4
gpt4 key购买 nike

我想对一对便利功能进行一些健全性测试,这些便利功能将一个64位整数分成两个32位整数,或者相反。目的是您不要进行移位,而逻辑运算会再次出现错字的可能性。健全性测试应该能100%确保这对功能虽然微不足道,但确实可以按预期工作。

没什么,真的……所以,我首先添加了以下内容:

static constexpr auto joinsplit(uint64_t h) noexcept { auto [a,b] = split(h); return join(a,b); }
static_assert(joinsplit(0x1234) == 0x1234);

...效果很好,但比我想要的要穷。当然,我可以再进行另外5或6种不同模式的测试,然后复制粘贴以进行救援。但是认真的...让编译器在一个很小的函数中检查十几个值不是很好吗?没有复制粘贴?现在那太酷了。

使用递归可变参数模板,可以做到这一点(这是我在缺少更好的东西的情况下使用的东西),但是我认为这很丑陋。

鉴于 constexpr函数和基于范围的 for的强大功能,拥有一些不错的可读性不是很酷:
    constexpr bool test()
{
for(constexpr auto value : {1,2,3}) // other numbers of course
{
constexpr auto [a,b] = split(value);
static_assert(value == join(a,b));
}
return true; // never used
}
static_assert(test()); // invoke test

该解决方案的一大优点是,除了可读性更高之外,从失败的 static_assert显而易见,不仅测试通常失败,而且测试失败的确切值也是如此。

但是,由于以下两个原因,此方法不起作用:
  • 您不能将value声明为constexpr,因为如编译器所言:“__for_begin的值不能在常量表达式中使用”。编译器也解释了其原因:“注意:__for_begin未声明为constexpr”。足够公平,这是一个原因,可能是愚蠢的。
  • 不能将
  • 分解声明声明为constexpr(对于static_assert错误,其后会立即出现非constexpr条件)。

  • 在这两种情况下,我都想知道是否允许 constexpr确实存在障碍。我了解为什么它不起作用(请参阅上文!),但是有趣的问题是为什么会这样?

    我承认将 value声明为 constexpr是一个谎言,因为它的值显然不是恒定的(每次迭代都不同)。另一方面,它所取的任何值都来自编译时常数值集,但是如果没有 constexpr关键字,编译器将拒绝将其视为该值,即 split的结果是非 constexpr且不能与 static_assert一起使用确实是。
    好吧,好吧...我是否真的想问太多,如果我想将具有变化的值声明为常量。即使从某些 Angular 来看,如果它是恒定的,则在每次迭代的范围内。不知何故...语言在这里缺少概念吗?

    我承认基于范围的 for就像lambdas一样,实际上只是一种黑客,它大多数都能起作用,并且大多数都是无形的,而不是真正的语言功能-提及 __for_begin是对其实现的死胡同。我也承认,通常的 for循环中的计数器是 constexpr,这通常很棘手(禁止),这不仅是因为它不是常量,而且因为原则上您可以在其中拥有任何类型的表达式,并且确实不能轻易地将其告知预先会生成一般的值(无论如何,在编译期间都不会花费合理的精力)。
    另一方面,给定一个确切的字面量有限序列(尽可能获得编译时常数),编译器应该能够进行多次迭代,循环的每次迭代都具有不同的编译时常数值(如果需要,请展开循环)。以某种方式(以非递归模板)的方式,这种事情应该可行吗?
    我在那里问得太多吗?

    我承认分解声明不是完全“琐碎的”事情。例如,可能需要在元组上调用 get,该元组是一个类模板(原则上可以是任何东西)。但是,无论如何, get恰好是 constexpr(所以这不是借口),并且在我的具体示例中,返回了具有两个成员的匿名结构的匿名临时变量,因此使用了公共(public)直接成员绑定(bind)(绑定(bind)到 constexpr struct)。
    具有讽刺意味的是,在第一个示例中,编译器甚至也做了正确的事情(以及递归模板)。因此,显然,这是很有可能的。仅出于某种原因,第二个示例中没有。
    同样,我在这里问得太多吗?

    正确的答案可能是“标准未提供该答案”。

    除此之外,还有什么真正的,技术上的原因使它不能,不能或不应该起作用?是疏忽大意,实现不足还是有意禁止的?

    最佳答案

    我无法回答您理论上的问题(“语言在这里缺少概念吗?”,“这种事情应该存在吗?我在那里问得太多了吗?”,“有任何真实的,技术上的原因使之不能,不能,还是不起作用?这是疏忽大意,实现不足还是有意禁止的?”)但是,从实践的 Angular 来看...

    With a recursive variadic template, this can be done (and it's what I'm using in lack of something better), but it's in my opinion needlessly ugly.



    我认为可变参数模板是正确的方法,并且(您标记了C++ 17)使用折叠功能时,没有理由对其进行递归化。

    举个例子
    template <uint64_t ... Is>
    static constexpr void test () noexcept
    { static_assert( ((joinsplit(Is) == Is) && ...) ); }

    以下是完整的编译示例
    #include <utility>
    #include <cstdint>

    static constexpr std::pair<uint32_t, uint32_t> split (uint64_t h) noexcept
    { return { h >> 32 , h }; }

    static constexpr uint64_t join (uint32_t h1, uint32_t h2) noexcept
    { return (uint64_t{h1} << 32) | h2; }

    static constexpr auto joinsplit (uint64_t h) noexcept
    { auto [a,b] = split(h); return join(a, b); }

    template <uint64_t ... Is>
    static constexpr void test () noexcept
    { static_assert( ((joinsplit(Is) == Is) && ...) ); }

    int main()
    {
    test<1, 2, 3>();
    }

    -编辑-奖励答案

    折叠(C++ 17)很棒,但是永远不会低估逗号运算符的功能。

    您可以在C++ 14中使用辅助函数和未使用的数组的初始化获得相同的结果(很好。。。)
    template <uint64_t I>
    static constexpr void test_helper () noexcept
    { static_assert( joinsplit(I) == I, "!" ); }

    template <uint64_t ... Is>
    static constexpr void test () noexcept
    {
    using unused = int[];

    (void)unused { 0, (test_helper<Is>(), 0)... };
    }

    显然,在对 joinsplit()进行了少许更改以使其符合C++ 14之后
    static constexpr auto joinsplit (uint64_t h) noexcept
    { auto p = split(h); return join(p.first, p.second); }

    关于c++ - 范围和分解的原因不允许constexpr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47377258/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com