【问题标题】:What should I use instead of templated enums?我应该使用什么来代替模板化枚举?
【发布时间】:2018-05-30 09:12:57
【问题描述】:

在我的代码中,我使用如下内容:

enum {
    foo = sizeof(some_type_t) < 32 ? 1 : 2,
    bar = some_constexpr_function() - N,
    baz = foo + bar,
    // etc.
}

对于我想确保没有运行时存储的一堆编译时常量;我有一些使用这些常量的函数。 N 是我们从其他地方得到的一些常数。

现在我想将我的代码模板化:N 不再是一个全局常量,而是一个数字模板参数。现在,如果我这样做:

template <size_t N> f()
{
    enum {
        foo = sizeof(some_type_t) < 32 ? 1 : 2,
        bar = some_constexpr_function() - N,
        baz = foo + bar,
        // etc.
    }
}

那行得通;但是 - 它限制了我在单个函数中使用这些常量;我想要几个。在 C++ 中,我们不能拥有

    template <size_t N>
    enum {
        foo = sizeof(some_type_t) < 32 ? 1 : 2,
        bar = some_constexpr_function() - N,
        baz = foo + bar,
        // etc.
    }

什么是实现这种模板化等效的优雅或惯用方式?

【问题讨论】:

  • 你如何期望它与匿名枚举一起工作
  • 我曾经做过一些similar stuff
  • 我真的不明白这里的枚举器有什么意义。只需定义普通常量。 “我不希望有任何运行时存储”这句话有点奇怪,因为如果您要将它们用于某些运行时计算,它们很可能会按值复制。
  • @Sopel:我不知道。这就是我问这个问题的原因。

标签: c++ templates enums idioms


【解决方案1】:

也许重载逗号 constexprly 会满足您的需求?

#include <utility>

enum params {
    foo = 1,
    bar = 2,
    baz = 3
};


struct some_type_t { };
constexpr std::size_t some_constexpr_function() {
    return 0;
}


constexpr std::size_t operator ,(params P, std::size_t N) {
    switch (P) {
        case foo:
            return sizeof(some_type_t) < 32 ? 1 : 2;
        case bar:
            return some_constexpr_function() - N;
        case baz:
            return (foo, N) - (bar, N);
    }
    return 0;
}


template <std::size_t>
struct example { };

int main() {
    example<(baz, 2)>{};
}

[live demo]

【讨论】:

  • 感谢您的想法。我发现许多开括号-闭括号的存在有点令人困惑。
  • @einpoklum overloading comma 怎么样? :)
  • 对原创性表示敬意,但是 - 这意味着在 N 上已经模板化的函数中没有自动模板参数解析的机会。如果我们忽略那些想要了解正在发生的事情的人的地狱......
  • @einpoklum 自动模板参数解析?你的意思是像c ++ 17的演绎指南或其他一些构造?无论哪种方式-我认为您的虚拟包装器也不具备这种能力,或者我是否缺少某些东西...(?)至于可理解性-人们决定什么成为成语,而您不会与成语争论:)
【解决方案2】:

到目前为止,我所做的是将我的枚举放在一个虚拟的持有者类中,可以模板化:

template <size_t N>
struct params
{
    enum {
        foo = sizeof(some_type_t) < 32 ? 1 : 2,
        bar = some_constexpr_function() - N,
        baz = foo + bar,
    };
}

但后来我不得不到处写params::fooparams::bar,这很乏味:

do_stuff_with(params::foo, params::bar)

而不是

do_stuff_with(foo, bar);

【讨论】:

    【解决方案3】:

    就像你在 cmets 中被告知的那样,我认为坚持将这些命名常量放在一个枚举之下是没有意义的。只需在单个命名空间范围内拥有一堆 constexpr 变量。您可以获得相同的净效果:

    namespace constants {
        namespace detail {
                    using underlying_t = int;
        }
        template<size_t N>
        constexpr detail::underlying_t foo = sizeof(some_type_t) < 32 ? 1 : 2;
    
        template<size_t N>
        constexpr detail::underlying_t bar = some_constexpr_function() - N;
    
        template<size_t N>
        constexpr detail::underlying_t baz = foo<N> + bar<N>;
    }
    

    它们都是constexpr,因此它们很容易成为运行时常量,如果需要,您可以在一个地方控制底层类型。此外,您可以通过完全限定的 id 访问它们,或者使用 using 指令将它们全部引入。

    【讨论】:

    • 这些可能有运行时存储。我想避免这种情况。
    • @einpoklum - 你被误导了。
    • 你能给我一个链接吗?
    • @einpoklum - 当然,这里是gcc at -O0。您会注意到变量没有存储空间。 esi 直接用1 加载。
    • 该示例因更改 a single character 而中断。现在,你可以争辩说“好吧,你不应该使用参考”——你可能是对的,但这甚至没有给我一个警告。我想要一个没有存储空间的保证。现在 - 不要误会我的意思,我不反对你的方法,这不是我想要的。
    猜你喜欢
    • 1970-01-01
    • 2021-10-21
    • 2010-11-05
    • 2010-12-06
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多