2

The following code:

#include<iostream>
#include<array>

using std::size_t;
using std::array;

//#define INIT_BY_HAND 1
#define CONSTEXPR 1

#ifdef CONSTEXPR
constexpr
#endif
size_t accumulator_count()
{
    constexpr size_t nb_rows{2};
    constexpr size_t nb_cols{2};
    size_t nb{0};
    for(size_t k = 0; k < nb_rows; ++k)
    {
        array<size_t,nb_cols> acc{}; // value initialization: expect both elements to be zero
        //for const_cast/static_cast see http://stackoverflow.com/questions/34199774/why-is-non-const-stdarrayoperator-not-constexpr
    #ifdef INIT_BY_HAND
        for(size_t j = 0; j < nb_cols; ++j)
            const_cast<size_t&>(static_cast<array<size_t,nb_cols> const&>(acc)[j]) = 0;
    #endif
        if(k == 0)
            const_cast<size_t&>(static_cast<array<size_t,nb_cols> const&>(acc)[0]) = 1;
        if(k == 1)
            const_cast<size_t&>(static_cast<array<size_t,nb_cols> const&>(acc)[nb_cols-1]) = 1;
        for(size_t j = 0; j < nb_cols; ++j)
            nb += size_t(static_cast<array<size_t,nb_cols> const&>(acc)[j] != 0);
    }
    return nb;
}

int main()
{
#ifdef CONSTEXPR
    constexpr
#endif
    size_t nb{accumulator_count()};
    std::cout << "nb: " << nb << std::endl;
}

compiles without warnings on gcc 5.3.1 (-Wall -std=c++14), but it produces the wrong output 3.

Without constexpr (comment out the line #define CONSTEXPR 1) it correctly outputs 2.

With initialization by hand (comment in the line #define INIT_BY_HAND 1) it correctly outputs 2.

Compiled with clang 3.8.0 it correctly outputs 2.

Question: Does this code conform to the c++14 standard or what did I miss?

robisson
  • 29
  • 1

1 Answers1

3

g++ 6.3.0 and clang++ 3.8.0 both compile and generate the value 2.

live example.

This strongly suggests that g++ 5.3.1 is in error.

As an aside:

namespace notstd {
  template<class T, std::size_t N>
  constexpr T& constexpr_get( std::array<T, N>& arr, std::size_t I ) {
    return const_cast<T&>(const_cast<std::array<T,N> const&>(arr)[I]);
  }
  struct at {
    std::size_t I = 0;
    constexpr at(std::size_t i):I(i){}
    template<class T, std::size_t N>
    friend constexpr T&
    operator->*( std::array<T, N>& arr, at a ) {
      return constexpr_get(arr, a.I);
    }
    // optional
    template<class T, std::size_t N>
    friend constexpr T const&
    operator->*( std::array<T, N> const& arr, at a ) {
      return arr[a.I];
    }
  };
}

and using notstd::at gives you a prettier infix way than all those casts:

arr->*at(0) = 1;

(who says we don't already have extension methods in C++?)

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524