17

Is there any other way to receive a reference to an array from function returning except using a pointer?

Here is my code.

int ia[] = {1, 2, 3};
decltype(ia) &foo() {   // or, int (&foo())[3]
    return ia;
}

int main() {
    int *ip1 = foo();   // ok, and visit array by ip1[0] or *(ip1 + 0)
    auto ip2 = foo();   // ok, the type of ip2 is int *
    int ar[] = foo();   // error
    int ar[3] = foo();  // error
    return 0;
}

And a class version.

class A {
public:
    A() : ia{1, 2, 3} {}
    int (&foo())[3]{ return ia; }
private:
    int ia[3];
};

int main() {
    A a;
    auto i1 = a.foo();    // ok, type of i1 is int *, and visit array by i1[0]
    int i2[3] = a.foo();  // error
    return 0;
}

Note: const qualifier is omitted in code.

I know the name of the array is a pointer to the first element in that array, so using a pointer to receive is totally viable.

Sorry, I made a mistake. From Array to pointer decay

There is an implicit conversion from lvalues and rvalues of array type to rvalues of pointer type: it constructs a pointer to the first element of an array.

Please ignore that XD

I'm just curious about the question I asked at the beginning :)

9
  • 2
    You cannot copy-initialize arrays. Returning references to arrays is a red herring; that is allowed and works fine. Commented Dec 23, 2015 at 15:52
  • 4
    name of the array is not a pointer to the first element, but can be implicitly converted to. There is a difference and this common mistake leads to errors. Anyway you better use std::array to avoid confusion. Commented Dec 23, 2015 at 15:55
  • 2
    "I know the name of the array is a pointer to the first element in that array" - this is wrong. Read this. EDIT: Ah, Slava beat me to it. The link is still a good read, though. Commented Dec 23, 2015 at 15:57
  • Oh, you mean in the class version? I've compiled the code in VS2015 and no error occurred (except the line I commented error), then how can I initialize the array? @KerrekSB Commented Dec 23, 2015 at 16:07
  • @Jaege: You can only copy-list-initialize, direct-list-initialize, value-initialize and default-initialize arrays, I think. Commented Dec 23, 2015 at 16:08

5 Answers 5

23

Is there any other way to receive a reference to an array from function returning except using a pointer?

Yes, using a reference to an array, like with any other type:

int (&ref)[3] = a.foo();

To avoid the clunky syntax, you could use a typedef instead.

typedef int int_array3[3];

...
int_array3& foo() { return ia; }

...

int_array3& ref = a.foo();
Sign up to request clarification or add additional context in comments.

1 Comment

auto& ref = a.foo(); is also possible
14

You should use std::array to avoid confusion and make cleaner, safer and less clunky code:

class A {
public:
    typedef std::array<int,3> array;
    A() : ia{1, 2, 3} {}
    array &foo(){ return ia; }
private:
    array ia;
};

int main() {
    A a;
    auto i1 = a.foo();      // ok, type of i1 is A::array, it is a copy and visit array by i1[0]
    for ( int i : i1 ) {}   // you can iterate now, with C array you cannot anymore
    auto &i2 = a.foo();     // ok, type of i2 is A::array&, you can change original by i2[0] = 123 
    A::array i3 = a.foo();  // fine, i3 is now a copy and visit array by i3[0]
    A::array &i4 = a.foo(); // fine, you can change original by i4[0] = 123
    int *i5 = a.foo().data();  // if you want old way to pass to c function for example, it is safer you explicitly show your intention
    return 0;
}

I know the name of the array is a pointer to the first element in that array

This is incorrect, array can be implicitly converted to a pointer to the first element. It is not the same.

13 Comments

This is quite misleading because it gives the impression that a reference to an array cannot be returned, or that the return value cannot be used.
@juanchopanza Where did I say that?
I didn't mean that you say it. Just that by concentrating on an std::array solution without mentioning that it is perfectly fin to return and use a reference to an array this gives the impression that you can't do it.
It is not perfectly fine to return reference to array, as this leads to poor and difficult to understand code. This is C++ first of all, C arrays are clunky as you mentioned in your answer
@Slava, no problem ... I might also suggest you send a proposal to ISO C++ committee to banish references to native arrays, too. If you are quick enough your proposal might become part of C++20.
|
3

Some alternatives I think are superior in C++14 onwards:

auto and array reference trailing return type:

template <typename T, std::size_t N>
auto array_ref_test(T(&array)[N]) -> T(&)[N]
{
    return array;
}

decltype(auto):

template <typename T, std::size_t N>
decltype(auto) array_ref_test(T(&array)[N])
{
    return array;
}

Example usage:

int array_ref_ [] = { 1, 2, 3, 4, 5 };   
decltype(auto) array_ref_result = array_ref_test(array_ref_); //'decltype(auto)' preserves reference unlike 'auto'
std::cout << std::size(array_ref_result);

Comments

1

To answer this question one can design a solution around std::reference_wrapper. This code compiles and runs.

#pragma once
#include <array>
#include <functional>
#include <cstdlib>
#include <ctime>

namespace {

/// <summary>
/// some generic utility function 
/// with argument declared as 
/// native array reference
/// for testing purposes only
/// </summary>
template<typename T, std::size_t N >
inline void array_util_function( T (&arr_ref) [N] ) {
    auto random = [](int max_val, int min_val = 1) -> int {
        // use current time as seed for 
        // random generator, but only once
        static auto initor = []() { 
            std::srand((unsigned)std::time(nullptr)); return 0; 
        }();

        return min_val + std::rand() / ((RAND_MAX + 1u) / max_val);
    };

    for (auto & element : arr_ref) {
        element = random(N);
    }
}

// the key abstraction
template <typename T, std::size_t N>
using native_arr_ref_wrapper = std::reference_wrapper<T[N]>;

template<typename T, std::size_t N >
constexpr std::size_t array_size(T(&arr_ref)[N]) {
    return N;
}

// return the size of the native array
// contained within
template<typename T, std::size_t N >
constexpr std::size_t array_size(native_arr_ref_wrapper<T,N> & narw_) {
    return array_size(narw_.get());
}

/// <summary>
/// returns std::reference_wrapper copy
/// that contains reference to native array 
/// in turn contained inside the std::array
/// argument
/// </summary>
template<typename T, std::size_t N >
    inline auto
        native_arr_ref(const std::array<T, N> & std_arr)
        -> native_arr_ref_wrapper<T,N>
{
    using nativarref = T(&)[N];

    return std::ref(
            (nativarref)*(std_arr.data())
        );
}

    /// <summary>
    /// returns std::reference_wrapper copy
    /// that contains reference to native array 
    /// </summary>
    template<typename T, std::size_t N >
    inline auto
        native_arr_ref(const T (& native_arr)[N] )
        -> native_arr_ref_wrapper<T, N>
    {
        using nativarref = T(&)[N];

        return std::ref(
            (nativarref)*(native_arr)
        );
    }

    auto printarr = [](auto arr_ref, const char * prompt = "Array :") {
        printf("\n%s:\n{ ", prompt);
        int j = 0;
        // use the array reference 
        // to see the contents 
        for (const auto & element : arr_ref.get()) {
            printf(" %d:%d ", j++, element);
        }
        printf(" }\n");
    };

// arf == native array reference
void test_ () {

using std::array;
// the arf type is 
// std::reference_wrapper<int[10]> 
auto arf  = native_arr_ref(array<int, 10>{});
// the arf type is same here 
// but made from a native array
auto ar2 = native_arr_ref({ 0,1,2,3,4,5,6,7,8,9 });
printarr(ar2);

// notice how argument to native_arr_ref()
// does not exist here any more
// but, wrapper contains the copy 
// of the native array  

// type is here: int[10]&
auto & native_arr_ref = arf.get();

// due to array type decay 
// the type is here: int *
auto native_arr_ptr = arf.get();

// size_t
const auto size = array_size(arf);

// this function requires
// native array as argument
// this is how we use arf in place of 
// native arrays
array_util_function(arf.get());

printarr(arf);

    }
  }

Comments

1

Your function's signature would look like this:

int(&A::adapter(/*input_args_or_empty*/))[3]{
   return ia;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.