强制表达式为 constexpr

Force expression to 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;
}

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

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

如果你定义count并在foo()中修改它,count(如foo()函数中所见)是一个变量,而不是常量。

所以你需要在另一个地方修改它:在一个constexpr函数中,所以返回值变成一个compile-time已知常量。

如果你可以使用 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 {}; }

对于 C++14 及更高版本,如果您的目标是 "merge" 主体,您可以简单地在函数模板中定义一个类型:

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,你也可以在常量表达式中使用lambdas,所以你可以将boo缩短为:

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

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

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

话虽如此,总的来说,我会说通常的(更简洁的)方法是在 header 中使用模板使用的各种额外代码,但这些代码不属于 public 接口将它们放在 detail 或类似命名的命名空间中:

namespace detail {
    template <char... C>
    constexpr int boo()
    {
        /* ... */
    }
}

template <char... C>
constexpr auto foo()
{
    /* ... detail::boo<C...>() ... */
}