为什么std :: array的推导指南不允许使用不同的类型?

时间:2018-06-20 11:04:44

标签: c++ arrays templates c++17 template-deduction

std::array的推导指南要求所有类型都相同:

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

此要求背后的原理是什么?如果允许使用不同类型的产品,会不会有明显的弊端?例如:

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

3 个答案:

答案 0 :(得分:10)

substantial design issues个正在使用common_type。例如,std::common_type_t<A, B, C>std::common_type_t<C, A, B>std::common_type_t<C, B, A>不必全部都存在-如果存在,则不必是同一类型:

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>);

在对初始化器的元素进行重新排序时,会导致“有趣的”用户体验,从而推论出不同的类型(或发出错误)。

答案 1 :(得分:6)

它与函数模板参数的推导方式匹配。

  

如果它们产生的推导次数多于一个for(let key in document){ let isEnumerable = document.propertyIsEnumerable(key); console.log(`docment['${key}'] isEnumerable?:${isEnumerable}`); },则类型推导失败。

[temp.deduct.call]/5

例如

WWW-Authenticate: NTLM

但是您可以提供常见类型的推导指南。

A

或转发给常见类型的重载

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')
}

答案 2 :(得分:1)

编辑::在下面的评论中,Daniel指出他确实在问,为什么一个单一类型的std::array不能被包含以下项的列表初始化:不同的类型(例如两个int和一个double)。不是为什么std::array必须是单一类型。

它不能满足Daniel对基本原理的要求,但是某些C ++环境支持实验\ array标头和std::experimental::make_array类,它们确实允许初始化不同类型的std :: array。可以推导或指定数组类型...

#include <array>
#include <experimental/array>

// creates array of doubles 
auto arr = std::experimental::make_array(1, 2, 3.4);

// creates array of ints
auto ra = std::experimental::make_array<int> (1, 2, 3.4);

ideone runnable sample


  

这种要求背后的原理是什么?

该限制是满足数组容器的语义所必需的。

例如,cppreference就是这样……

  

此容器是一种聚合类型,其语义与将C样式数组T[N]作为其唯一的非静态数据成员的结构相同。

很明显,没有一种类型超过 T 的C样式数组。

从另一个角度来看:cplusplus这样说…

  

容器属性

     

序列
  顺序容器中的元素按严格的线性顺序排序。各个元素按其在此序列中的位置进行访问。

     

连续存储
  元素存储在连续的内存位置中,允许对元素进行恒定时间的随机访问。元素的指针可以偏移以访问其他元素。

     

固定大小的聚合
  容器使用隐式构造函数和析构函数静态地分配所需的空间。它的大小是编译时常数。没有内存或时间开销。

为使指针运算有意义,每个元素必须具有相同的宽度。为了使指针更有意义(在指针算术之后),每个元素必须使用相同的位表示形式-否则,您可能会发现自己将IEEE浮点表示形式视为是二进制补码整数(反之亦然)。


说“因为标准如此”并不能真正满足您的基本要求,但确实可以(我认为)。我找不到实际标准的可引用副本,但是a working draft of the C++ standard表示了这一点……

  

26.3.7.2数组构造函数,复制和分配[array.cons]
  合计(11.6.1)的条件应得到满足。类数组依赖于隐式声明的特殊成员函数(15.1、15.4和15.8),以符合26.2中的容器要求表。除了容器需求表中指定的需求之外,数组的隐式move构造函数和move赋值运算符还分别要求T为MoveConstructible或MoveAssignable。

template<class T, class... U>  
  array(T, U...) -> array<T, 1 + sizeof...(U)>;`   
     

要求: (is_same_v<T, U> && ...)是正确的。否则程序格式错误。