为什么不std :: array :: size static? [英] Why isn't std::array::size static?

查看:119
本文介绍了为什么不std :: array :: size static?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

std :: array 在编译时是已知的,但是 size 成员函数不是静态的。有什么原因吗?稍微不方便,不能在不实例化对象的情况下计算大小。 (嗯,我知道 std :: tuple_size 专业化,但它不适用于从 std :: array派生的类。)

没有很好的理由。 =http://www.boost.org/doc/libs/1_60_0/doc/html/array.html =nofollow> boost :: array< T,N> std :: array 的前缀实际上定义了 static size_t size(){return N;} (虽然现代版本应该使用 constexpr )。



同意OP,这是一个不幸的遗漏和语言特征的欠绘制。在我看来0),你可以从 std :: array (这是一个讨论旧的C ++,这是更合理的如果你的派生类不添加成员变量),1) std :: array 应该有一个 static size_t size()成员函数,2)



如果您同意这一点,那么解决方法如下: std
如果您不同意,您可以在这里停止阅读。



问题


$ b b

我面对这个问题之前,逻辑导致的解决方案。 OP的情况如下:你有一个类从 std :: array 派生,你需要在编译时访问大小。

 #include< array> 

template< class T ...>
struct myarray:std :: array<取决于T ...> {
...非常酷的功能...
};

以后您有

  template< class Array,size_t N => 
functionOnArrayConcept(Array const& a){...}

N 在编译时。



现在,没有代码 std :: array myarray 可以写的 std :: tuple_size< myarray< ...>> 将无法使用。



解决方案



(这是@TC建议的在编译时访问最大模板深度?。我只是在这里复制它。)

  template< class T,std :: size_t N> 
auto array_size_impl(const std :: array< T,N&)
- > std :: integral_constant< std :: size_t,N> ;;

template< class Array>
using array_size = decltype(array_size_impl(std :: declval< const Array&>()));

template< class Array>
constexpr auto static_size() - > decltype(array_size< Array> :: value){
return array_size< Array> :: value;
}
template< class Array>
constexpr auto static_size(Array const&) - > decltype(static_size< Array>()){
return static_size< Array>();
}

现在您可以使用:

  template< class Array,size_t N = static_size< Array>()> 
functionOnArrayConcept(Array const& a){...}

code> std :: tuple_size 已经,不幸的是(我想)你需要为每个派生类专门化 std :: tuple_size

  namespace std {
template< class ... T& //如果myarray没有被类参数化,可能会更复杂
struct tuple_size< myclass< T ...>> :integral_constant< size_t,static_size< myclas< T ...>()> {};
}

(在我看来,这是由STL设计中的另一个错误, code> std :: tuple_size< A> 没有默认的模板< class A> struct tuple_size:A :: size(){} 。)







TC
解决方案。

b
$ b

如果函数从你的类中解耦,你必须使用 std :: tuple_size ,因为这是访问大小的唯一标准方法 std :: array 。因此,你必须这样做,1)提供 std :: tuple_size 的专业化,如果你可以控制 myclass 2) std :: array 没有 static size(),但是你的派生类可以)。



因此,这可以是在STD框架中的一个相当一般的解决方案,它包含 std :: tuple_size
(不幸的是,在 std :: 中提供特殊化有时是唯一的方法来创建真正的通用代码。参见 http://en.cppreference.com/w/cpp/language/extending_std

  template< class ... T> 
struct myarray:std :: array< ...某些依赖于T ...> {
...非常酷的函数...
static constexpr size_t size(){ return std :: tuple_size< std :: array< ...取决于T ... ...>> :: value;}
};

namespace std {
// std :: tuple_size的特殊化为std :: array< ...>`。
template< class ... T> //如果myarray没有被类参数化,可能会更复杂
struct tuple_size< myclass< T ...>> :integral_constant< size_t,myclass< T ...> :: size()> {};
}

//现在`functionOnArrayConcept`也可用于`myarray`。

static size_t size()并且可以有其他方法来推导 myarray 的基础大小,而不向 size 。)



注意



在编译器中,不工作。如果这个工作,整个讨论将不那么重要,因为 std :: tuple_size 不是那么必要。

  template< class ArrayConcept,size_t N = ArrayConcept {}。size()> //错误非法表达式,`std :: declval< ArrayConcept>()`也不工作。 
functionOnArrayConcept(ArrayConcept const& a){...}

Conceptualization



由于 std :: array 的实现(或规范?)的缺点提取编译时间 size 的唯一方式是通过 std :: tuple_size std :: array 必要的接口的<$ c $> std :: tuple_size 。因此,当你继承 std :: array 时,你在某种意义上也有inherit std :: tuple_size 。不幸的是,你需要这样做进一步推导。这是此答案背后的概念。



解决方案2(GNU hack)



如果你使用GNU的STD库(包括 gcc clang ),使用而不添加任何代码,这是通过使用 :: _ AT_Type :: _ Type (成员)类型的 _M_elems std :: array< T,N> 。<$ c>(也称为 T [N] p>

此函数将有效地表现为一个静态函数 :: size()(除了它不能用于一个对象) std :: array 或从 std :: array 派生的任何类型。

  std :: extent< typename ArrayType :: _ AT_Type :: _ Type> :: value 
  

template< class ArrayType>
constexpr size_t array_size(){
return std :: extent< typename ArrayType :: _ AT_Type :: _ Type> :: value
}
pre>

这个工作是因为继承了成员类型 _AT_Type :: _ Type 。 (我不知道为什么GNU离开这个实现细节 public 。另一个省略?)



解决方案3便携式hack)



最后,使用模板递归的解决方案可以找出基础的维度 std :: array

  template< class Array,size_t N = 0,bool B = std :: is_base_of< std :: array< typename Array :: value_type,N>,Array> :: value> 
struct size_of:size_of< Array,N + 1,std :: is_base_of< std :: array< typename Array :: value_type,N + 1>,Array> :: value&

template< class Array,size_t N>
struct size_of< Array,N,true> :std :: integral_constant< size_t,N> {};

//这是'static Array :: size()'的替换
template< class Array,size_t N = size_of< Array> :: value>
constexpr size_t static_size(){return N;}

//这个版本可以使用`static Array :: size()`来调用
template< class array,size_t N = size_of< Array> :: value>
constexpr size_t static_size(Array const&){return N;}

将获得:

  struct derived:std :: array< double,3> {}; 

static_assert(static_size< std :: array< double,3>>()== 3);
static_assert(static_size< derived>()== 3);
constexpr derived d;
static_assert(static_size(d)== 3);

如果使用某种类型调用此函数与 std :: array ,它会给出递归错误。如果你想要一个软错误,你必须添加专业化。

  template< class Array& 
struct size_of< Array,250,false> {};

其中 250 代表大量小于递归限制。 (我不知道如何自动获得这个数字,我只知道我的编译器中的递归限制 256 。)


The size of std::array is known at compile time, but the size member function isn't static. Is there any reason for that? It's slightly inconvenient not to be able to calculate the size without instantiating an object. (Well, I know about std::tuple_size specialization, but it doesn't work for classes derived from std::array.)

解决方案

There is no good reason for that. In fact, boost::array<T, N>, the precursor of std::array<T,N>, actually defines static size_t size(){return N;} (although a modern version should use constexpr also).

I agree with the OP that this is an unfortunate omission and underexplotaition of the language features. In my opinion 0) you can derive from std::array (this is a discussion as old as C++, this is even more justified if your derived class doesn't add member variables), 1) std::array should have a static size_t size() member function, 2) specializations inside namespace std are justified.

If you agree with this, the workaround is the following. If you don't agree you can stop reading here.

Problem

I faced this problem before and the logic leads to couple of solution. The OP situation is the following: you have a class that derives from std::array and you need to access to the size at compile time.

#include<array>

template<class T...>
struct myarray : std::array< something that depends on T... >{
    ... very cool functions...
};

and later you have

template<class Array, size_t N = ???>
functionOnArrayConcept(Array const& a){...}

Where you need to know N at compile time.

As it is now, there is no code ??? that you can write that works both for std::array and myarray, because std::tuple_size<myarray<...>> will not work.

Solution

(this was suggested by @T.C. here Access maximum template depth at compile? . I am just copying it here.)

template<class T, std::size_t N>
auto array_size_impl(const std::array<T, N>&) 
    -> std::integral_constant<std::size_t, N>;

template<class Array>
using array_size = decltype(array_size_impl(std::declval<const Array&>()));

template<class Array>
constexpr auto static_size() -> decltype(array_size<Array>::value){
    return array_size<Array>::value;
}
template<class Array>
constexpr auto static_size(Array const&) -> decltype(static_size<Array>()){
    return static_size<Array>();
}

Now you can use it as this:

template<class Array, size_t N = static_size<Array>()>
functionOnArrayConcept(Array const& a){...}

If you are using std::tuple_size already, unfortunately (I think) you need to specialize std::tuple_size for each of your derived classes:

namespace std{
    template<class... T> // can be more complicated if myarray is not parametrized by classes only
    struct tuple_size<myclass<T...>> : integral_constant<size_t, static_size<myclas<T...>>()>{};
}

(In my opinion this is caused by another mistake in the STL design that std::tuple_size<A> doesn't have the default template<class A> struct tuple_size : A::size(){}.)


The solutions beyond this point are near obsolete compared to @T.C. solution described above. I'll keep them here for reference only.

Solution 1 (idiomatic)

If the function is decoupled from you class you have to use std::tuple_size because that is the only standard way of accessing the size of std::array at compile time. Therefore you have to do this, 1) provide a specialization of std::tuple_size and if you can control myclass, 2) std::array doesn't have static size() but your derived class could (that simplifies the solution).

So, this can be a pretty general solution within the framework of STD, that consists in the specialization of std::tuple_size. (Unfortunately providing specialization in std:: sometimes is the only way to make real generic code. See http://en.cppreference.com/w/cpp/language/extending_std)

template<class... T>
struct myarray : std::array<...something that depends on T...>{
    ... very cool functions...
    static constexpr size_t size(){return std::tuple_size<std::array<...something that depends on T...>>::value;}
};

namespace std{
    // specialization of std::tuple_size for something else that `std::array<...>`.
    template<class... T> // can be more complicated if myarray is not parametrized by classes only
    struct tuple_size<myclass<T...>> : integral_constant<size_t, myclass<T...>::size()>{};
}

// now `functionOnArrayConcept` works also for `myarray`.

(static size_t size() can be called differently, and there may be other ways to deduce the size of the base of myarray without adding any static function to size.)

Note

In the compilers I tried the following trick doesn't work. If this worked, the whole discussion would be less important, because std::tuple_size wouldn't be so necessary.

template<class ArrayConcept, size_t N = ArrayConcept{}.size()> // error "illegal expression", `std::declval<ArrayConcept>()` doesn't work either.
functionOnArrayConcept(ArrayConcept const& a){...}

Conceptualization

Due to this shortcoming in the implementation (or specification?) of std::array by which the only way to extract the compile time size is through std::tuple_size. Then std::tuple_size is conceptually part of the necessary interface of std::array. Therefore when you inherit from std::array you have also "inherit" std::tuple_size in some sense. And unfortunately you need to do this for further derivations. This is the concept behind this answer.

Solution 2 (a GNU hack)

If you are using GNU's STD library (that includes gcc and clang), there is a hack that can be used without adding any code, and that is by using the _M_elems member which is of (member) type ::_AT_Type::_Type (a.k.a. type T[N]) of std::array<T, N>.

This function will effectively behave like a static function ::size() (except that it cannot be used for instances of an object) of std::array or any type derived from std::array.

std::extent<typename ArrayType::_AT_Type::_Type>::value

which can be wrapped into:

template<class ArrayType>
constexpr size_t array_size(){
    return std::extent<typename ArrayType::_AT_Type::_Type>::value
}

This work because the member type _AT_Type::_Type is inherited. (I wonder why GNU left this implementation detail public. Another omission?)

Solution 3 (a portable hack)

Finally, a solution using template recursion one can figure out what is the dimension of the base std::array.

template<class Array, size_t N=0, bool B = std::is_base_of<std::array<typename Array::value_type, N>, Array>::value>
struct size_of : size_of<Array, N + 1, std::is_base_of<std::array<typename Array::value_type, N+1>, Array>::value>{};

template<class Array, size_t N>
struct size_of<Array, N, true> : std::integral_constant<size_t, N>{};

// this is a replacement for `static Array::size()`    
template<class Array, size_t N = size_of<Array>::value>
constexpr size_t static_size(){return N;}

// this version can be called with an object like `static Array::size()` could
template<class Array, size_t N = size_of<Array>::value>  
constexpr size_t static_size(Array const&){return N;}

This is how one will get:

struct derived : std::array<double, 3>{};

static_assert( static_size<std::array<double, 3>>() == 3 );
static_assert( static_size<derived>() == 3 );
constexpr derived d;
static_assert( static_size(d) == 3 );

If this function is called with some type unrelated to std::array, it will give a recursion error. If you want a "soft" error instead, you have to add the specialization.

template<class Array>
struct size_of<Array, 250, false> {}; 

where 250 stands for a large number but smaller than the recursion limit. (I don't know how to get this number automatically, I only know the the recursion limit in my compiler is 256.)

这篇关于为什么不std :: array :: size static?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆