代码之家  ›  专栏  ›  技术社区  ›  HolyBlackCat

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

  •  3
  • HolyBlackCat  · 技术社区  · 7 年前

    我在学一些神秘的东西 有状态模板元编程
    (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);
    }
    

    Does C++ support compile-time counters? ,但其中大多数都局限于名称空间范围,其他的也同样不适用于gcc8。

    什么我 find是一个简单的概念证明可设置的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.)

    我怎样才能修好它?

    0 回复  |  直到 7 年前
        1
  •  6
  •   HolyBlackCat    6 年前

    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();
            }
        };
    }
    
    推荐文章