c++gccoperator-overloadingc++20valarray

Is it a bug of the implementation of std::valarray in gcc?


I tried the following program

#include <iostream>
#include <valarray>

int main( void ) 
{
    std::valarray<int> v1 = { 1, 2, 3, 4, 5 };
    std::valarray<int> v2 = { 1, 2, 3, 4, 5 };

    auto v3 = v1 * v2;

    for ( const auto &item : v3 ) std::cout << item << ' ';
    std::cout << '\n';

    return 0;
}

and got an error message that an appropriate function begin for v3 used implicitly in this statement

    for ( const auto &item : v3 ) std::cout << item << ' ';

can not be found.

So I tried the following code

#include <iostream>
#include <valarray>
#include <type_traits>

int main( void ) 
{
    std::valarray<int> v1 = { 1, 2, 3, 4, 5 };
    std::valarray<int> v2 = { 1, 2, 3, 4, 5 };

    auto v3 = v1 * v2;

    std::cout << std::is_same<std::valarray<int>, decltype( v3 )>::value << '\n';
    return 0;
}

and got the result

0

But when this statement

auto v3 = v1 * v2;

is changed to

std::valarray<int> v3 = v1 * v2;

then the output is

1

The operator * for std::valarray<int> is declared the following way

template<class T> valarray<T> operator* (const valarray<T>&, const valarray<T>&);

So is it a bug of the implementation of std::valarray<int>?


Solution

  • This is not a bug. std::valarray::operator* does not actually have to return a std::valarray because it is allowed to use expression templates. That means it can return a type that has the following properties:

    • All const member functions of std::valarray are provided.
    • std::valarray, std::slice_array, std::gslice_array, std::mask_array and std::indirect_array can be constructed from the replacement type.
    • All functions accepting an argument of type const std::valarray& except begin() and end() (since C++11) should also accept the replacement type.
    • All functions accepting two arguments of type const std::valarray& should accept every combination of const std::valarray& and the replacement type.
    • The return type does not add more than two levels of template nesting over the most deeply-nested argument type.

    emphasis mine source

    Because of this, you need to explicitly capture the return as a std::valarray so the specialization for std::begin can be called.