3

I was playing around with std::valarray and UndefinedBehaviorSanitizer, and noticed that std::begin an empty std::valarray causes undefined behavior. Here is the code:

#include <valarray>

int main() {
    std::valarray<int> a;
    std::begin(a);
}

To reproduce, compile the code with g++ -fsanitize=undefined and run the result executable.

Here is the std::begin implementation from libstdc++.

template<class _Tp>
  inline _Tp*
  begin(valarray<_Tp>& __va)
  { return std::__addressof(__va[0]); }

It seems that _val[0] creates an reference of null value for empty std::valarrays which causes the undefined behavior.

Is this a bug from libstdc++?

bolov
  • 72,283
  • 15
  • 145
  • 224
EFanZh
  • 2,357
  • 3
  • 30
  • 63
  • 1
    of interest: https://stackoverflow.com/questions/17796200/is-begin-end-for-any-empty-vector – bolov Dec 03 '20 at 05:14
  • The [cppreference page](https://en.cppreference.com/w/cpp/numeric/valarray/begin2) says that the intent of begin(a) "is to allow range for loops to work with valarrays". This would imply that begin(a)==end(a) for an empty valarray. However, the standard doc I've looked at (N4861) doesn't say this, just that begin returns "An iterator referencing the first value in the array" with no indicator of what happens with an empty valarray. `__va[0]` violates the Precondition for operator[] with an empty valarray. – 1201ProgramAlarm Dec 03 '20 at 05:35

1 Answers1

3

n4659 - C++17 final working draft

§26.2.1 General container requirements [container.requirements.general]

Table 83 — Container requirements

a.begin() - no precondition

so a.begin() must be valid on an empty container.

However valarray is defined in §29.7 Numeric arrays [numarray] from the §29 Numerics library [numerics] chapter so it's not directly part of the container library chapter.

The std::begin(valarray) is defined in §29.7.10 valarray range access [valarray.range] and here there is no mention of preconditions. Most relevant quotes hare are:

§ 29.7.10 valarray range access [valarray.range]

  1. The iterators returned by begin and end for an array are guaranteed to be valid until the member function resize(size_t, T) (29.7.2.8) is called for that array or until the lifetime of that array ends, whichever happens first.

     template <class T> unspecified 1 begin(valarray<T>& v);
     template <class T> unspecified 2 begin(const valarray<T>& v);
    
  2. Returns: An iterator referencing the first value in the array.

So the question is if Table 83 applies here. valarray is described in §29.7.2 Class template valarray [template.valarray] and the standard says:

29.7.2.1 Class template valarray overview [template.valarray.overview]

  1. The class template valarray is a one-dimensional smart array,

Which seems to me to imply that valarray is a container falling under §26.2.1 General container requirements

To me it looks like std::begin on an empty valarray should be valid. On the other hand "Returns: An iterator referencing the first value in the array" could imply a precondition that the valarray must not be empty. So my only conclusion is that the standard should be more clear in this regard.

bolov
  • 72,283
  • 15
  • 145
  • 224