How to declare array with auto

前端 未结 4 689
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-05 01:03

I have been playing with auto and I noticed that for most cases you can replace a variable definition with auto and then assign the type.

I

相关标签:
4条回答
  • 2020-12-05 01:35

    Not quite the same, but you could use array:

    auto z = std::array<int, 5>();
    
    0 讨论(0)
  • 2020-12-05 01:44

    decltype works with g++ 4.9.0 20130601 for this:

    #include <iostream>
    #include <algorithm>
    
    static std::ostream& logger = std::clog;
    
    class A {
        static int _counter;
        int _id;
      public:
        A() : _id(++_counter) {
            logger << "\tA #" << _id << " c'tored\n";
        } 
    
        ~A() {
            //logger << "\tA #" << _id << " d'tor\n";
        } 
    
        inline int id() const{
            return _id;
        }
    };
    int A::_counter(0); 
    
    std::ostream& operator<<(std::ostream& os, const A& a) {
        return os << a.id();
    }
    
    int main() {
    
        auto dump = [](const A& a){ logger << a << " ";};
    
        logger << "x init\n";
        A x[5]; 
        logger << "x contains: "; std::for_each(x, x+5, dump);
    
        logger << "\ndecltype(x) y init\n";
        decltype(x) y;
        logger << "y contains: ";  std::for_each(y, y+5, dump);
        logger << std::endl;
    
        return 0;
    }
    

    Output:

    x init
        A #1 c'tored
        A #2 c'tored
        A #3 c'tored
        A #4 c'tored
        A #5 c'tored
    x contains: 1 2 3 4 5 
    decltype(x) y init
        A #6 c'tored
        A #7 c'tored
        A #8 c'tored
        A #9 c'tored
        A #10 c'tored
    y contains: 6 7 8 9 10 
    
    0 讨论(0)
  • 2020-12-05 01:45

    Not exactly the same thing, and it's a bit ugly, but it is possible to deduce the element type from a list initializer and declare the array directly, as follows:

    template<typename T>
    struct array_trait
    {
        using element_type = T;
        array_trait(T(&&)[]);
    };
    
    decltype(array_trait({4,5,7}))::element_type a[] = {4,5,7};
    
    std::cout << typeid(a).name() << std::endl;
    
    for (unsigned i = 0; i < 3; i++)
        std::cout << a[i] << std::endl;
    

    The type should be int[3] and the output should be 4 5 7.

    0 讨论(0)
  • 2020-12-05 01:50

    TL;DR

    template<typename T, int N> using raw_array = T[N];
    
    auto &&z = raw_array<int,5>{};
    

    Your example of auto z = int[5]; isn't legal any more than auto z = int; is, simply because a type is not a valid initializer. You can write: auto z = int{}; because int{} is a valid initializer.

    Once one realizes this, the next attempt would be:

    auto z = int[5]{};
    

    Note that your int y[5] does not have any initializer. If it had then you would have jumped straight here.

    Unfortunately this does not work either for obscure syntax reasons. Instead you must find a legal way to name the array type in an initializer. For example, a typedef name can be used in an initializer. A handy reusable template type alias eliminates the burdensome requirement of a new typedef for every array type:

    template<typename T, int N> using raw_array = T[N];
    
    auto z = raw_array<int,5>{};
    

    Aside: You can use template type aliases to fix the weird 'inside-out' syntax of C++, allowing you to name any compound type in an orderly, left-to-right fashion, by using this proposal.


    Unfortunately due to the design bug in C and C++ which causes array-to-pointer conversions at the drop of a hat, the deduced type of the variable z is int* rather int[5]. The resulting variable becomes a dangling pointer when the temporary array is destroyed.

    C++14 introduces decltype(auto) which uses different type deduction rules, correctly deducing an array type:

    decltype(auto) z = raw_array<int,5>{};
    

    But now we run into another design bug with arrays; they do not behave as proper objects. You can't assign, copy construct, do pass by value, etc., with arrays. The above code is like saying:

    int g[5] = {};
    int h[5] = g;
    

    By all rights this should work, but unfortunately built-in arrays behave bizarrely in C and C++. In our case, the specific problem is that arrays are not allowed to have just any kind of initializer; they are strictly limited to using initializer lists. An array temporary, initialized by an initializer list, is not itself an initializer list.


    Answer 1:

    At this point Johannes Schaub makes the excellent suggestion that we can use temporary lifetime extension.

    auto &&z = raw_array<int,5>{};
    

    decltype(auto) isn't needed because the addition of && changes the deduced type, so Johannes Schaub's suggestion works in C++11. This also avoids the limitation on array initializers because we're initializing a reference instead of an array.

    If you want the array to deduce its length from an initializer, you can use an incomplete array type:

    template<typename T> using unsized_raw_array = T[];
    
    auto &&z = unsized_raw_array<int>{1, 2, 3};
    

    Although the above does what you want you may prefer to avoid raw arrays entirely, due to the fact that raw arrays do not behave like proper C++ objects, and the obscurity of their behavior and the techniques used above.

    Answer 2:

    The std::array template in C++11 does act like a proper object, including assignment, being passable by value, etc., and just generally behaving sanely and consistently where built-in arrays do not.

    auto z = std::array<int,5>{};
    

    However, with this you miss out on being able to have the array type infer its own length from an initializer. Instead You can write a make_array template function that does the inference. Here's a really simple version I haven't tested and which doesn't do things you might want, such as verify that all the arguments are the same type, or let you explicitly specify the type.

    template<typename... T>
    std::array<typename std::common_type<T...>::type, sizeof...(T)>
    make_array(T &&...t) {
        return {std::forward<T>(t)...};
    }
    
    auto z = make_array(1,2,3,4,5);
    
    0 讨论(0)
提交回复
热议问题