std::array
的大小在编译时是已知的,但size
成员函数不是静态的。有什么理由吗?在没有实例化对象的情况下不能计算大小有点不方便。 (嗯,我知道std::tuple_size
专业化,但它不适用于从std::array
派生的类。)
答案 0 :(得分:6)
从C ++ 11开始,您可以在std :: array上使用std :: tuple_size来获取编译时常量的大小。参见
答案 1 :(得分:5)
没有充分的理由。事实上,std::array<T,N>
的前身boost::array<T, N>
实际上定义了static size_t size(){return N;}
(虽然现代更有用的版本也应该使用constexpr
。
我同意OP,这是一个令人遗憾的遗漏和语言特征的低估。
<强>问题强>
我之前遇到过这个问题,逻辑导致了几个解决方案。 OP情况如下:您有一个派生自std::array
的类,您需要在编译时访问该大小。
#include<array>
template<class T...>
struct myarray : std::array< something that depends on T... >{
... very cool functions...
};
以后你有
template<class Array, size_t N = ???>
functionOnArrayConcept(Array const& a){...}
您需要在编译时了解N
。
就像现在一样,您可以编写的代码???
同时适用于std::array
和myarray
,因为std::tuple_size<myarray<...>>
无效。
解决方案
(这是@ T.C。Access maximum template depth at compile?建议的。我只是在这里复制它。)
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){...}
如果您已经使用std::tuple_size
,遗憾的是(我认为)您需要为每个派生类专门化std::tuple_size
:
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...>>()>{};
}
(在我看来,这是由于STL设计中的另一个错误导致std::tuple_size<A>
没有默认template<class A> struct tuple_size : A::size(){}
。)
与@ T.C相比,超越这一点的解决方案已经过时了。 上述解决方案。我会把它们留在这里仅供参考。
解决方案1(惯用语)
如果函数与您的类解耦,则必须使用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<...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()
可以被不同地调用,并且可能有其他方法可以推导myarray
的基数大小而不向size
添加任何静态函数。)
注意强>
在编译器中我尝试了以下技巧不起作用。如果这样做有效,那么整个讨论就不那么重要了,因为std::tuple_size
不是那么必要。
template<class ArrayConcept, size_t N = ArrayConcept{}.size()> // error "illegal expression", `std::declval<ArrayConcept>()` doesn't work either.
functionOnArrayConcept(ArrayConcept const& a){...}
<强>概念化强>
由于std::array
的实现(或规范?)中的这个缺点,提取编译时间size
的唯一方法是通过std::tuple_size
。然后std::tuple_size
概念是std::array
必要界面的一部分。因此,当您从std::array
继承时,您在某种意义上也“继承”std::tuple_size
。不幸的是,您需要这样做以进一步推导。这是这个答案背后的概念。
解决方案2(GNU黑客)
如果您正在使用GNU的STD库(包括gcc
和clang
),则可以使用hack而无需添加任何代码,即使用_M_elems
属于::_AT_Type::_Type
的(成员)类型T[N]
(又名类型std::array<T, N>
)的成员。
此函数的行为类似于静态函数::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
}
这项工作是因为成员类型_AT_Type::_Type
是继承的。 (我想知道为什么GNU离开了这个实现细节public
。另一个遗漏?)
解决方案3(便携式黑客)
最后,使用模板递归的解决方案可以找出基础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;}
这是一个人将得到的:
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
。)
答案 2 :(得分:1)
它确实可以是静态的,但是,这会破坏“容器”接口,这种接口无法与期望容器具有size()
成员函数的其他通用算法一起使用。但是,没有什么可担心的,因为std::array::size()
是constexpr
函数,因此绝对没有与之相关的开销。
更新:
先生。 Jrok指出可以用“普通”语法调用静态成员函数。下面是一个例子,它不会:
#include <array>
struct array {
static unsigned int size()
{
return 0;
}
};
template <typename T>
static auto do_stuff(T& data) -> decltype(data.size())
{
typedef decltype(data.size()) size_type;
size_type (T::*size_calc)() const = &T::size;
size_type n = 0;
for (size_type i = 0, e = (data.*size_calc)(); i < e; ++i)
++n;
return n;
}
int main()
{
// Below is fine:
std::array<int, 5> data { 1, 2, 3, 4, 5 };
do_stuff(data);
// This, however, won't work as "size()" is not a member function.
#if 0
array fake;
do_stuff(fake);
#endif
}
答案 3 :(得分:0)
array::size
是constexpr
,因此除非存储的类型具有构造函数或析构函数,否则操作array_t().size()
不太可能具有任何运行时效果。您可以将其嵌入模板参数中以确保它不会。但它确实看起来像运行时代码。
我认为它只是为了与其他容器的一致性而非静止。例如,您可以为其创建指向成员的函数。然而,发现任何事物的真正理由通常都需要进行艰苦的研究。可能是作者从未想过它。
我想到的另一件事是,operator () ()
之类的一些特殊函数不能是静态的,所以静态的任何机会性应用都只能是零碎的。通用问题可以更好地以统一的方式解决,即使这意味着改变核心语言。
答案 4 :(得分:-1)
请注意,Microsoft Visual C ++目前不支持constexpr(http://msdn.microsoft.com/en-us/library/hh567368.aspx),因此以下有效代码不起作用:
array<int,3> dog;
array<double, dog.size( )> cat;
以下类提供了编译时静态变量:
/**
* hack around MSVC's 2012 lack of size for const expr
*/
template <typename T, int N>
struct vcarray : public std::array<T,N> {
static const size_t ArraySize= N;
};
可以用作:
vcarray<double,3> cat;
vcarray<double,cat.ArraySize> dog;
答案 5 :(得分:-3)
在我看来,只要size
成员函数static
没有提供附加价值就没有意义。它可以成为static
,但你从中获得的一切都没有。
array
类的设计方式,您可以查询给定array
对象的大小,而无需在需要的位置明确知道/记住其确切类型(包括其大小)尺寸。这是一种方便,它消除了制作复制/编辑错误的机会。您可以编写如下代码:
std::array<int, 5> blah;
// 50 lines of code
do_something_with(blah.size()); // blah knows its size
正如你所看到的,在我消耗数组大小的位置,我实际上并不记得它是什么,但我的代码无论如何都会工作,无论实际值是什么,无论是否有一个我将数组的类型更改为不同的大小
由于size
函数只返回一个模板参数,编译器可以简单地证明返回值是编译时常量并相应地进行优化(函数也是constexpr
,所以你也可以使用返回值作为模板参数或枚举)。
现在,如果我们制作size
成员函数static
会有什么不同?
如果size
是static
函数,您可以仍然以完全相同的方式使用静态成员函数(即,在对象实例上,在“不是静态的方式“),但那将是”作弊“。毕竟,无论成员是static
还是array
,这都已经有效了
此外,您现在可以在没有对象实例的情况下调用成员函数。虽然乍一看这似乎是一件好事但static
类模板(...其中返回的大小是模板参数)确实没有任何优势。
为了在没有对象的情况下调用成员函数(即,以“std::array<int, 5> blah;
// 50 lines of code
do_something_with(std::array<int,5>::size()); // I must tell size what to return
成员函数方式”),您必须使用类名及其正确的模板参数正确限定函数。
换句话说,你必须写一些类似的东西:
size
现在我们通过调用{{1}}函数获得了什么?什么都没有。为了调用该函数,我们需要提供正确的模板参数,其中包含的大小。
这意味着我们必须提供我们希望查询的信息。调用函数并没有告诉我们任何我们不知道的事情。