在GCC 8上运行的Constexpr计数器,不限于名称空间范围 [英] Constexpr counter that works on GCC 8, and is not restricted to namespace scope

查看:70
本文介绍了在GCC 8上运行的Constexpr计数器,不限于名称空间范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试学习一些不可思议的状态模板元编程技巧。

(这就是我要学习的原因。不幸的是,该库在GCC 8和Clang上均不起作用。)

I'm trying to learn some arcane stateful template metaprogramming tricks.
(Here's why I want to learn it. Unfortunately this library doesn't work on GCC 8 nor on Clang.)

我首先需要的是 constexpr 计数器:

The first obvious thing I need is a constexpr counter:

/*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);
}

最好是一个 tagged 计数器,因此我可以同时拥有多个计数器:

Preferably it should be a tagged counter, so that I can have several counters at the same time:

/*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。

I did some research, but alas, none of the counters I found worked with GCC 8.

  • https://github.com/DaemonSnake/unconstexpr - Works with GCC 7, doesn't work with GCC 8 nor with Clang 6. (Try it online.)
  • http://b.atch.se/posts/constexpr-counter/ - Once again, works with GCC 7 and doesn't work with GCC 8 nor with Clang 6. (Try it online.)

我还在这里找到了一些实现: C ++是否支持编译时计数器?,但是其中大多数限制在名称空间范围内,而其他限制再次与GCC 8不兼容。

I also found some implementations here: Does C++ support compile-time counters?, but most of them are limited to namespace scope, and others once again don't work with GCC 8.

没有发现的是一个简单的概念验证可设置的constexpr标志: http://b.atch.se/posts/non-constant-constant-expressions/

What I did find was a simple proof-of-concept settable constexpr flag: 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);

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

This one is not tagged, i.e. you can only have one per translation unit, which is not good.

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

I've managed to write my own tagged implementation based on it:

用法:

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;
        }
    };
}

(尝试一下。)

接下来,我尝试进行实际计数。

Next, I tried to make an actual counter.

所需用法:

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 。)

My naïve attempt: (For some reason it stops at 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;
        }
    };
}

(尝试一下。)

我该如何解决?

推荐答案

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

The body of a constexpr function template must yield the same answer for all instatiations with the same template parameters and same arguments. You need to add a level of indirection, so the calculation can happen in the default argument of a template parameter dependent on the first.

请参见 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();
        }
    };
}

这篇关于在GCC 8上运行的Constexpr计数器,不限于名称空间范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆