Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why deduction guide for std::array does not allow different types?

The deduction guide for std::array requires all types be the same:

std::array arr = { 1, 2, 3.4 }; // error

What is the rationale behind such a requirement? Would there be any significant drawback if different types were allowed instead? For example:

namespace std {
   template <typename... T>
   array(T...) -> array<std::common_type_t<T...>, sizeof...(T)>;
}

std::array arr = { 1, 2, 3.4 }; // decltype(arr)::value_type deduced as double
like image 393
Daniel Langr Avatar asked Jun 20 '18 11:06

Daniel Langr


2 Answers

There are substantial design issues with using common_type. For example, std::common_type_t<A, B, C>, std::common_type_t<C, A, B> and std::common_type_t<C, B, A> need not all exist - and if they do, need not be the same type:

struct A;
struct B;
struct C;
struct A { operator B(); };
struct B { operator C(); };
struct C { operator A(); };

static_assert(std::is_same_v<std::common_type_t<A, B, C>, C>);
static_assert(std::is_same_v<std::common_type_t<C, A, B>, B>);
static_assert(std::is_same_v<std::common_type_t<C, B, A>, A>);

That makes for an "interesting" user experience when reordering the elements of the initializer causes a different type to be deduced (or an error to be emitted).

like image 186
T.C. Avatar answered Oct 24 '22 07:10

T.C.


It matches how function template arguments are deduced.

If they yield more than one possible deduced A, the type deduction fails.

[temp.deduct.call]/5

e.g.

template<typename T>
void foo(T, T){}

template<typename T>
struct bar{ bar(T, T) {} };

int main()
{
    foo(1, 1.5); // error, note:   deduced conflicting types for parameter 'T' ('int' and 'double')
    bar(1, 1.5); // error, note:   deduced conflicting types for parameter 'T' ('int' and 'double')
}

But you can provide a deduction guide for common types.

template<typename T>
struct baz{ baz(T, T) {} };

template<typename T, typename U>
baz(T, U) -> baz<std::common_type_t<T, U>>    

or overloads that forward to common types

template<typename T>
void quux(T, T){}

template<typename T, typename U>
std::enable_if_t<!std::is_same<std::decay_t<T>, std::decay_t<U>>> quux(T t, U u) 
{ 
    using C = std::common_type_t<T, U>; 
    quux<C>(std::forward<C>(t), std::forward<C>(u)); // I think this is right
}

int main()
{
    baz(1, 1.5);
    quux(1, 1.5);
}
like image 38
Caleth Avatar answered Oct 24 '22 05:10

Caleth