Принудительное выражение для constexpr - PullRequest
2 голосов
/ 23 июня 2019

Учитывая две функции 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 ]

5 голосов
/ 23 июня 2019

Проблема в том, что 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 {}; }
4 голосов
/ 23 июня 2019

Для 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,вы также можете использовать лямбда-выражения в константных выражениях, поэтому вы можете сократить boo до:

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

В C ++ 20 вы сможете писать лямбда-выражения непосредственно в качестве аргумента шаблона, так что вы можетеуменьшите далее до (если вы действительно хотели бы):

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

Сказав это, в общем, я бы сказал, что обычный (и более чистый) подход имеет все виды дополнительныхкод, используемый шаблонами в заголовке, но не являющийся частью общедоступного интерфейса, помещает их в пространство имен detail или с аналогичным именем:

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

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