std::array
的大小在编译时是已知的,但是size
成员函数不是静态的。这有什么原因吗?如果不示例化一个对象,就不能计算大小,这有点不方便。(嗯,我知道std::tuple_size
的专门化,但是它不适用于从std::array
派生的类。)
std::array
的大小在编译时是已知的,但是size
成员函数不是静态的。这有什么原因吗?如果不示例化一个对象,就不能计算大小,这有点不方便。(嗯,我知道std::tuple_size
的专门化,但是它不适用于从std::array
派生的类。)
7条答案
按热度按时间ffvjumwh1#
There is no good reason for that. In fact,
boost::array<T, N>
, the precursor ofstd::array<T,N>
, actually definesstatic size_t size(){return N;}
(although a modern more useful version should useconstexpr
also).I agree with the OP that this is an unfortunate omission and underexplotaition of the language features.
Problem
I faced this problem before and the logic leads to a couple of solutions. 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.and later you have
Where you need to know
N
at compile time.As it is now, there is no code
???
that you can write that works both forstd::array
andmyarray
, becausestd::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.)
Now you can use it as this:
If you are using
std::tuple_size
already, unfortunately (I think) you need to specializestd::tuple_size
for each of your derived classes:(In my opinion this is caused by another mistake in the STL design that
std::tuple_size<A>
doesn't have the defaulttemplate<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 ofstd::array
at compile time. Therefore you have to do this, 1) provide a specialization ofstd::tuple_size
and if you can controlmyclass
, 2)std::array
doesn't havestatic 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 instd::
sometimes is the only way to make real generic code. See http://en.cppreference.com/w/cpp/language/extending_std )(
static size_t size()
can be called differently, and there may be other ways to deduce the size of the base ofmyarray
without adding any static function tosize
.)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.Conceptualization
Due to this shortcoming in the implementation (or specification?) of
std::array
by which the only way to extract the compile timesize
is throughstd::tuple_size
. Thenstd::tuple_size
is conceptually part of the necessary interface ofstd::array
. Therefore when you inherit fromstd::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
andclang
), 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. typeT[N]
) ofstd::array<T, N>
.This function will effectively behave like a static function
::size()
(except that it cannot be used for instances of an object) ofstd::array
or any type derived fromstd::array
.which can be wrapped into:
This work because the member type
_AT_Type::_Type
is inherited. (I wonder why GNU left this implementation detailpublic
. 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
.This is how one will get:
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.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 is256
.)dtcbnfnu2#
从C++11开始,你可以在
std::array
上使用std::tuple_size
来获取大小作为编译时常量。http://en.cppreference.com/w/cpp/container/array/tuple_size
mv1qrgav3#
It can indeed be static, however, this would break "container" interface which won't play well with other generic algorithms that do expect containers to have
size()
member function. There is nothing to worry about, though, asstd::array::size()
is aconstexpr
function, so there is absolutely no overhead associated with it.UPDATE:
Mr. Jrok have pointed out that one can call static member functions with "normal" syntax. Below is an example when it won't:
pcww981p4#
array::size
就是constexpr
,所以除非存储类型有构造函数或析构函数,否则操作array_t().size()
不太可能有任何运行时影响。您可以将它嵌入到模板参数中以确保它不会有任何影响。不过,它在其他方面看起来确实像运行时代码。我认为它是非静态的,仅仅是为了与其他容器保持一致。例如,你可以构造一个指向它的成员函数的指针。然而,发现任何东西的真正原理往往需要坚韧的研究。可能是作者从来没有想到过这一点。
另一个想到的是一些特殊的函数,比如
operator () ()
,不能是静态的,所以任何静态的机会主义应用都只能是零碎的,通用问题最好以统一的方式解决,即使这意味着要改变核心语言。ghg1uchk5#
You can re-declare a same-typed empty
std::array
(which should get optimized out) and take the size of that. For example:compiles to:
[No issues found when using
size_of_some_array
as a template parameter: which was counter-alluded to in the comments on @Potatoswatter's answer.]vhipe2zx6#
Note that the Microsoft Visual C++ doesn't currently support constexpr ( http://msdn.microsoft.com/en-us/library/hh567368.aspx ), so the following valid code won't work:
The following class provides a compile time static variable:
which can be used as:
9bfwbjaz7#
In my opinion it does not make sense to make the
size
member functionstatic
insofar as it provides no added value. It is possible to make itstatic
, but you gain nothing from it.The way the
array
class is designed, you can query the size of a givenarray
object without explicitly knowing/remembering its exact type (which includes its size) at that location where you need the size. This is a convenience, and it removes the opportunity to make copy/edit errors. You can write code like this:As you can see, at the location where I'm consuming the array's size, I don't actually remember what it was, but my code will work anyway, regardless of what the value actually is, and regardless whether maybe one day I change the array's type to be a different size.
Since the
size
function merely returns a template parameter, the compiler can trivially prove that the return value is a compile-time constant and optimize accordingly too (the function is alsoconstexpr
, so you can also use the return value as template parameter or enumeration).Now what will be different if we make the
size
member functionstatic
?If
size
was astatic
function, you could still use the static member function in the exact same way (that is, on an object instance, in a "not static way"), but that would be "cheating". After all, this is something that already works anyway, whether the member isstatic
or not.Further, you now have the possibility of invoking the member function without an object instance. While this seems like a good thing at first glance it really is no advantage at all for the
array
class template (...where the returned size is a template parameter).In order to call a member function without an object (that is, in a "
static
member function way"), you must properly qualify the function with the class name and its proper template parameters.In other words, you must write something like:
Now what have we gained from calling the
size
function? Nothing at all. In order to call the function, we needed to provide the correct template parameters, which includes the size.That means no more and no less than that we must supply the information that we wish to query. Calling the function doesn't tell us anything we didn't already know.