-1

In the C++ Standard there is the following deduction guide for the template class std::valarray<T>:

template<class T, size_t cnt> valarray(const T(&)[cnt], size_t) -> valarray<T>;

However among the constructors of the class there is only the following appropriate constructor (or am I mistaken?)

valarray(const T&, size_t);

However if to run the following demonstrative program with a similar deduction guide

#include <iostream>
#include <utility>

template <typename T>
struct A
{
    A( const T &, size_t ) { std::cout << "A<T>()\n"; }
};

template<class T, size_t cnt> 
A(const T(&)[cnt], size_t) -> A<T>;

int main()
{
    int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

    A obj( a, std::size( a ) );
}

the gcc compiler issues error

rog.cc:17:12: error: invalid conversion from 'int*' to 'int' [-fpermissive]
   17 |     A obj( a, std::size( a ) );
      |            ^
      |            |
      |            int*
prog.cc:7:8: note:   initializing argument 1 of 'A<T>::A(const T&, size_t) [with T = int; size_t = long unsigned int]'
    7 |     A( const T &, size_t ) { std::cout << "A<T>()\n"; }
      |        ^~~~~~~~~

So a question arises whether it is a defect of the C++ Standard, or a bug of the compiler or I missed something.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335

2 Answers2

1

Using the example given by https://en.cppreference.com/w/cpp/numeric/valarray/deduction_guides, we can look at the compiler output without optimizations to see which constructor is called:

int a[] = {1, 2, 3};
std::valarray va(a, 3);

https://godbolt.org/z/rtgeoi

main:
    [...]
    call    std::valarray<int>::valarray(int const*, unsigned long)
    [...]

C++ arrays decay to pointers to their first element very easily. However, without the deduction guide, the type deduced from the implicit guides would be std::valarray<int[3]> (the guide generated from valarray(const T&, size_t) wins because it does not need the array-to-pointer conversion). This can be demonstrated in your example, if we have both the A(const T&, std::size_t); and A(const T*, std::size_t); constructors:

template <typename T>
struct A
{
    A(const T&, std::size_t);
    A(const T*, std::size_t);
};

int main()
{
    int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    A obj(a, 10);
}

// Result:
    call    A<int [10]>::A(int const (&) [10], unsigned long)

https://godbolt.org/z/nlFlVT

Adding the deduction guide correctly deduces the intended int instead of int[10]: https://godbolt.org/z/efj557

Max Langhof
  • 23,383
  • 5
  • 39
  • 72
  • The problem is that the code does not compile using C++ gcc HEAD 10.0.0 20190 as I pointed in my question. If to add the second constructor with the parameter of a pointer type then this constructor is selected. – Vlad from Moscow Oct 08 '19 at 16:56
  • The code in your question does not compile because the user-defined deduction guide contradicts the only constructor. The `A::A(const int&, size_t)` constructor cannot take an `int[10]` as first argument. You need either both constructors and the deduction guide (what `valarray` has so that you don't accidentally deduce `A` because the `const T&` deduction beat `const T*`), or only the constructor from pointer (then you cannot construct from `count` copies of the same value, but you won't need a user-defined deduction guide). – Max Langhof Oct 08 '19 at 17:01
  • Even having two constructors the constructor A( const T *, size_t ) is selected. – Vlad from Moscow Oct 08 '19 at 17:04
  • @VladfromMoscow With the deduction guide, that's the intention (and makes the code [compile](https://godbolt.org/z/wuYsaB)). If you mean without deduction guide, then I [cannot reproduce](https://godbolt.org/z/A3B3Ys) that... Could you show the exact two-constructor code, compiler version and flags you get this with (preferably on godbolt)? – Max Langhof Oct 08 '19 at 17:06
0

However among the constructors of the class there is only the following appropriate constructor (or am I mistaken?)

There's also this one:

valarray( const T* vals, std::size_t count );

Which does match, when the array you pass in decays to a pointer, allowing the cppreference example to compile.

Barry
  • 286,269
  • 29
  • 621
  • 977