强制将表达式表达为constexpr

时间:2019-06-23 11:08:55

标签: c++ c++17 variadic-templates template-meta-programming constexpr

考虑到两个constexpr函数,是否可以将它们组合为一个函数?

template <char... C>
constexpr int boo()
{
    char ch[] = { C... };
    int count = 0;

    for (char c : ch)
    {
        if (c != '0') count += 1;
    }

    return count;
}

template <char... C>
constexpr auto foo()
{
    std::array<char, boo<C...>()> x{};

    return x;
}

如示例所示,我可以将'count'作为常量返回。 我的问题是我不能在声明的函数中使用'count'作为常量。也就是说,如果将“ boo()”的主体放在“ foo()”中,则编译器将抛出“ count”不是常量。

2 个答案:

答案 0 :(得分:5)

问题是std::array需要一个常量作为大小值。

如果您定义count并在foo()内对其进行修改,则count(如在foo()函数中所示)是变量,而不是常量。

因此,您需要在另一个地方进行修改:在constexpr函数中,以便返回的值成为编译时已知的常量。

如果您可以使用C ++ 17,那么可以进行模板折叠(对Evg和Rakete1111进行了改进;谢谢),您完全可以避免使用bar()

template <char... C>
constexpr auto foo()
{
    std::array<char, (0u + ... + (C != '0'))> x{};

    return x;
}

但是如果只有C ++ 11,则需要递归

template <typename = void>
constexpr std::size_t bar ()
 { return 0u; }

template <char C0, char ... C>
constexpr std::size_t bar ()
 { return bar<C...>() + (C0 == '0' ? 0u : 1u); }

template <char... C>
constexpr std::array<char, bar<C...>()> foo()
 { return {}; }

答案 1 :(得分:4)

对于C ++ 14及更高版本,如果您的目标是“合并”主体,则只需在函数模板内定义类型:

template <char... C>
constexpr auto foo()
{
    struct {
        constexpr int operator()() {
            char ch[] = { C... };
            int count = 0;

            for (char c : ch)
            {
                if (c != '0') count += 1;
            }

            return count;
        };
    } boo;

    std::array<char, boo()> x{};

    return x;
}

如果您具有C ++ 17,则还可以在常量表达式中使用lambda,因此可以将boo缩短为:

constexpr auto boo = []() { /* ... */ };

在C ++ 20中,您将能够直接将lambda表达式作为模板参数来编写,因此您可以进一步简化为(如果您确实想要的话):

std::array<char, []() { /* ... */ }()> x{};

总体而言,我要说的是,在标头中使用模板使用的所有各种额外代码但不属于公共接口的通常(也是更简洁的)方法是将它们放在{{ 1}}或类似名称的命名空间:

detail