1

I use the function below to create a list of the leap years between a range of years:

// dynamic initialization
std::vector<int> create_leap_years(const int start_year, const int end_year)
{
    std::vector<int> collection;
    for (auto year = start_year; year < end_year; year++)
    {
        const auto is_leap_year = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
        if (is_leap_year)
        {
            collection.emplace_back(year);
        }
    }
    return collection;
}

I try to learn constexpr and use it to create a static initialization of a list of the leap years for the specified range of years. Should I create a vector and then transform it in a std::array? If I try a function to return an fixed array like below:

// static initialization
static constexpr std::array<int,24>& create_leap_years(const int start_year, const int end_year)
{
    // Error: variable in constexpr function does not have automatic storage duration
    static const std::array<int, 24> collection = 
    { 1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936, 
      1940, 1944, 1948, 1952, 1956, 1960, 1964, 1968, 
     1972, 1976, 1980, 1984, 1988, 1992, 1996 };

    return collection;
}

Visual Studio 2019 returns the following error and I am not sure where to go from there:

Error: variable in constexpr function does not have automatic storage duration

Below a complete example of what I try to accomplish:

#include <iostream>    
#include <vector>
#include <array>

#if 1
// dynamic initialization
std::vector<int> create_leap_years(const int start_year, const int end_year)
{
    std::vector<int> collection;
    for (auto year = start_year; year < end_year; year++)
    {
        const auto is_leap_year = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
        if (is_leap_year)
        {
            collection.emplace_back(year);
        }
    }
    return collection;
}

#else
// static initialization
static constexpr std::array<int,24>& create_leap_years(const int start_year, const int end_year)
{
    // Error: variable in constexpr function does not have automatic storage duration
    static const std::array<int, 24> collection = 
    { 1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936, 
      1940, 1944, 1948, 1952, 1956, 1960, 1964, 1968, 
     1972, 1976, 1980, 1984, 1988, 1992, 1996 };

    return collection;
}
#endif

int main()
{
    const auto collection = create_leap_years(1900, 2000);

    for (auto year : collection) std::cout << year << " ";
    std::cout << std::endl;

    return 0;
}

Below the update for the function to return an array based on the feedback I got to return by the array by value:

std::array<int, 100> create_leap_years(const int start_year, const int end_year)
{
    static std::array<int, 100> collection{};

    auto idx = 0;
    for (auto year = start_year; idx < 100 && year < end_year; year++)
    {
        const auto is_leap_year = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
        if (is_leap_year)
        {
            collection[idx++] = year;
        }
    }
    return collection;
}
Less White
  • 561
  • 4
  • 13
  • 1
    Your `collection` isn't `constexpr, add `constexpr` to the definition, i.e. `static constexpr std::array ...`. – user975989 Aug 31 '19 at 14:45
  • The compiler VS2019 then tells the error: constexpr function 'create_leap_year' cannot result in a constant expression. Here the function with 2 items to make it shorter: static constexpr const std::array & create_leap_years(const int start_year, const int end_year) { static constexpr const std::array collection = { 1904, 1908 }; return collection; } – Less White Aug 31 '19 at 14:49
  • 1
    Right, whoops: https://stackoverflow.com/questions/38619259/literal-string-declared-static-in-constexpr-function – user975989 Aug 31 '19 at 14:54
  • 2
    Why do you need a function to begin with? You have a piece of const data, just expose it as such. – n. m. could be an AI Aug 31 '19 at 14:58
  • [Related](https://stackoverflow.com/questions/41723704/how-to-filter-a-stdinteger-sequence). – n. m. could be an AI Aug 31 '19 at 15:52

2 Answers2

1

You cannot have static variables in constexpr functions. This rule may be relaxed in the future.

Make the variable constexpr and not static. Return by value.

constexpr std::array<int,24> get_1900s_leap_years()
{
    constexpr std::array<int, 24> collection = 
    { 1904, 1908, 1912, 1916, 1920, 1924, 1928, 1932, 1936, 
      1940, 1944, 1948, 1952, 1956, 1960, 1964, 1968, 
     1972, 1976, 1980, 1984, 1988, 1992, 1996 };

    return collection;
}
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • I did remove the static but not sure where to add constexpr. See my update. If a put constexpr, I get the error: function call must have a constant value in expression. If I add const to the function create_leap_years, it tells me the expression did not evaluate to a const. – Less White Aug 31 '19 at 15:40
1

Just remove static keyword in collection's declaration line then put constexpr in function signature:

constexpr auto create_leap_years(const int start_year, const int end_year)
//^^^^^^^
{
    /*static*/ std::array<int, 100> collection{};
    auto idx = 0;
    for (auto year = start_year; idx < 100 && year < end_year; year++)
    {
        const auto is_leap_year = (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
        if (is_leap_year)
        {
            collection[idx++] = year;
        }
    }
    return collection;
}

This will work, see live demo.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90