Использование constexpr в качестве размера std :: array - PullRequest
4 голосов
/ 17 октября 2019

У меня есть следующий код, который использует constexpr в качестве размера массива.

#include <array>

template <size_t size>
constexpr size_t
GetSize(const char(&format)[size])
{
    // Iterate over format array and compute a value.
    size_t count = 0;
    for (size_t i = 0; i < size; ++i)
    {
        if (format[i] == '%')
        {
            ++count;
        }
    }

    return count;
}

template <size_t size>
constexpr auto
GetArray(const char(&format)[size])
{
    constexpr size_t arraySize = GetSize(format);
    return std::array<char, arraySize>();
}

int main(int argc, char** argv)
{
    static_assert(GetArray("hello").size() == 12, "failed");
}

Это, однако, не удается скомпилировать в VS2019 со следующей ошибкой

error C2131:  expression did not evaluate to a constant
message :  failure was caused by a read of a variable outside its lifetime
message :  see usage of 'format'
message :  see reference to function template instantiation 'auto GetArray<6>(const char (&)[6])' being compiled

Этоошибка компилятора? Если да, то есть ли обходной путь для этого?

Ответы [ 3 ]

1 голос
/ 18 октября 2019

Проблема с функциями constexpr заключается в том, что вы можете вызывать как с аргументами constexpr, так и с аргументами non-constexpr:

int constexpr f(int n)
{
    return ++n;
}

int constexpr n0 = 7;
int n1; std::cin >> n1;
f(n0); // result IS constexpr
f(n1); // result is NOT constexpr, just an ordinary int

Из-за этой характеристики сами параметры функции не могут быть constexpr, или, точнее, не может использоваться в контекстах constexpr. Так и в вашей функции:

constexpr size_t arraySize = getSize(format);
//                                      ^ cannot be used as constexpr, even if
//                                        constexpr has been passed to, so result
//                                        not either (the f(n1) case above)

Вы можете немного изменить свою вторую функцию:

template <size_t Size>
constexpr auto
getArray()
{
    return std::array<char, Size>();
}

И использовать ее как

int main(int argc, char** argv)
{
    static_assert(getArray<getSize("hello")>().size() == 0, "failed");
    return 0;
}

Конечно, выглядитдовольно уродливо, но вы можете спрятаться за макросом:

#define myGetArray(STRING) getArray<getSize(STRING)>()

или просто

#define getArray(STRING) std::array<char, getSize(STRING)>()
0 голосов
/ 18 октября 2019

Вот рабочее решение C ++ 17, использующее лямбду для переноса массива char в качестве constexpr.

#include <array>
#include <string>

template <typename Char_Array_Holder>
constexpr size_t GetSize(Char_Array_Holder holder)
{
  // Iterate over format array and compute a value.
  constexpr std::string_view format = holder();

  size_t count = 0;
  for (char c : format)
  {
    if (c == '%')
    {
      ++count;
    }
  }

  return count;
}

template <typename Char_Array_Holder>
constexpr auto GetArray(Char_Array_Holder holder)
{
  constexpr size_t arraySize = GetSize(holder);
  return std::array<char, arraySize>();
}

#define hold(msg) []() { return msg; }

int main(int argc, char** argv)
{
  static_assert(GetArray(hold("hello")).size() == 0, "failed...");
  static_assert(GetArray(hold("h%ello")).size() == 1, "failed...");
  static_assert(GetArray(hold("h%el%%lo")).size() == 3, "failed...");
}

Адаптировано из парсинга строки времени компиляции вы можете прочитать, чтобы получить дополнительную информацию

0 голосов
/ 17 октября 2019

Я не знаю, нужна ли вам функция GetSize() для каких-то других целей, но в вашем GetArray у вас уже есть доступ к длине массива, так что вы можете просто сделать это:

#include <array>

template <std::size_t N>
constexpr auto
GetArray(const char(&format)[N])
{
    return std::array<char, N*2>{};
}

int main(int argc, char** argv)
{
    static_assert(GetArray("hello").size() == 12, "failed");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...