gpt4 book ai didi

c++ - 在 GCC 8 上工作的 Constexpr 计数器,并且不限于命名空间范围

转载 作者:行者123 更新时间:2023-12-01 13:58:04 25 4
gpt4 key购买 nike

我正在尝试学习一些神秘的有状态模板元编程技巧。
(Here's why I want to learn it. Unfortunately this library doesn't work on GCC 8 nor on Clang.)

我需要的第一个明显的东西是 constexpr柜台:

/*something*/ constexpr int foo() /*something*/

int main()
{
constexpr int a = foo();
constexpr int b = foo();
constexpr int c = foo();
static_assert(a == 0 && b == 1 && c == 2);
}

最好它应该是一个标记的计数器,以便我可以同时拥有多个计数器:
/*something*/ constexpr int foo() /*something*/

struct TagA {};
struct TagB {};

int main()
{
constexpr int a = foo<TagA>();
constexpr int b = foo<TagA>();
constexpr int c = foo<TagA>();

constexpr int d = foo<TagB>();
constexpr int e = foo<TagB>();
constexpr int f = foo<TagB>();

static_assert(a == 0 && b == 1 && c == 2);
static_assert(d == 0 && e == 1 && f == 2);
}

我做了一些研究,但唉,我发现的计数器都没有与 GCC 8 一起使用。
  • https://github.com/DaemonSnake/unconstexpr - 适用于 GCC 7,不适用于 GCC 8 和 Clang 6。(Try it online.)
  • http://b.atch.se/posts/constexpr-counter/ - 再一次,适用于 GCC 7,不适用于 GCC 8 和 Clang 6。(Try it online.)

  • 我还在这里找到了一些实现: Does C++ support compile-time counters? ,但其中大多数仅限于命名空间范围,其他人再次不适用于 GCC 8。

    我确实发现了一个简单的概念验证可设置 constexpr 标志: http://b.atch.se/posts/non-constant-constant-expressions/
    /*something*/ constexpr bool foo() /*something*/

    constexpr bool a = foo();
    constexpr bool b = foo();
    constexpr bool c = foo();
    static_assert (a == 0 && b == 1 && c == 1);

    这个没有标记,即每个翻译单元只能有一个,这不好。

    我已经设法基于它编写了我自己的标记实现:

    用法:
    int main()
    {
    constexpr int c0_false = Meta::Flag<TagA>::ReadSet();
    constexpr int c0_true = Meta::Flag<TagA>::ReadSet(); // Will continue to return true after this point.
    static_assert(c0_false == 0);
    static_assert(c0_true == 1);

    constexpr int c1_false = Meta::Flag<TagB>::ReadSet();
    constexpr int c1_true = Meta::Flag<TagB>::ReadSet(); // Will continue to return true after this point.
    static_assert(c1_false == 0);
    static_assert(c1_true == 1);
    }

    执行:
    namespace Meta
    {
    template <typename T> class Flag
    {
    struct Dummy
    {
    constexpr Dummy() {}
    friend constexpr void adl_flag(Dummy);
    };

    template <bool> struct Writer
    {
    friend constexpr void adl_flag(Dummy) {}
    };

    template <class Dummy, int = (adl_flag(Dummy{}),0)>
    static constexpr bool Check(int)
    {
    return true;
    }

    template <class Dummy>
    static constexpr bool Check(short)
    {
    return false;
    }

    public:
    template <class Dummy = Dummy, bool Value = Check<Dummy>(0), int = sizeof(Writer<Value && 0>)>
    static constexpr int ReadSet()
    {
    return Value;
    }

    template <class Dummy = Dummy, bool Value = Check<Dummy>(0)>
    static constexpr int Read()
    {
    return Value;
    }
    };
    }

    (Try it live.)

    接下来,我尝试制作一个实际的计数器。

    预期用途:
    constexpr int c0 = Meta::TaggedCounter<TagA>::Value();
    constexpr int c1 = Meta::TaggedCounter<TagA>::Value();
    constexpr int c2 = Meta::TaggedCounter<TagA>::Value();
    static_assert(c0 == 0);
    static_assert(c1 == 1);
    static_assert(c2 == 2);

    我天真的尝试:(出于某种原因,它停在 1 。)
    namespace Meta
    {
    template <typename T> class TaggedCounter
    {
    template <int I> struct Tag {};

    public:
    template <int N = 0, bool B = Flag<Tag<N>>::ReadSet()> static constexpr int Value()
    {
    if constexpr (B)
    return 1 + Value<N+1>();
    else
    return 0;
    }
    };
    }

    (Try it live.)

    我该如何解决?

    最佳答案

    constexpr 函数模板的主体必须为具有相同模板参数和相同参数的所有实例产生相同的答案。您需要添加一个间接级别,因此计算可以发生在依赖于第一个模板参数的默认参数中。

    https://gcc.godbolt.org/z/GHfKKf

    namespace Meta
    {
    template <typename T,int I> struct Tag {};

    template<typename T,int N,bool B>
    struct Checker{
    static constexpr int currentval() noexcept{
    return N;
    }
    };

    template<typename T,int N>
    struct CheckerWrapper{
    template<bool B=Flag<Tag<T,N>>::Read(),int M=Checker<T,N,B>::currentval()>
    static constexpr int currentval(){
    return M;
    }
    };

    template<typename T,int N>
    struct Checker<T,N,true>{
    template<int M=CheckerWrapper<T,N+1>::currentval()>
    static constexpr int currentval() noexcept{
    return M;
    }
    };

    template<typename T,int N,bool B=Flag<Tag<T,N>>::ReadSet()>
    struct Next{
    static constexpr int value() noexcept{
    return N;
    }
    };

    template <typename T> class TaggedCounter
    {
    public:
    template <int N=CheckerWrapper<T,0>::currentval()> static constexpr int Value(){
    return Next<T,N>::value();
    }
    };
    }

    关于c++ - 在 GCC 8 上工作的 Constexpr 计数器,并且不限于命名空间范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51601439/

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