2015-12-23 3 views
7

Есть ли другой способ получить ссылку на массив из возвращаемой функции, за исключением использования указателя?Функция C++, возвращающая ссылку на массив

Вот мой код.

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; 
} 

И классная версия.

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; 
} 

Примечание: const спецификатор опущен в коде.

Я знаю, что имя массива является указателем на первый элемент в этом массиве, поэтому использование указателя для получения является полностью жизнеспособным.

Извините, я допустил ошибку. Из Array to pointer decay

Существует неявное преобразование из lvalues ​​и rvalues ​​типа массива к rvalues ​​типа указателя: он создает указатель на первый элемент массива.

Пожалуйста, игнорируйте, что XD

Я просто любопытно вопрос, я спросил в начале :)

+2

Вы не можете копировать-инициализировать массивы. Возвращение ссылок на массивы - это красная селедка; это разрешено и отлично работает. –

+4

имя массива не является указателем на первый элемент, но может быть неявно преобразовано в. Есть разница, и эта распространенная ошибка приводит к ошибкам. В любом случае вам лучше использовать 'std :: array', чтобы избежать путаницы. – Slava

+2

* «Я знаю, что имя массива является указателем на первый элемент в этом массиве» * - это неправильно. Прочитайте [this] (http://en.cppreference.com/w/cpp/language/array#Array_to_pointer_decay). EDIT: Ах, Слава избил меня. Ссылка все еще хорошо читается. –

ответ

10

Есть ли другой способ получить ссылку на массив из возвращаемой функции, за исключением использования указателя?

Да, используя ссылку на массив, как с любым другим типом:

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

Чтобы избежать неуклюжий синтаксис, вы могли бы использовать вместо typedef.

typedef int int_array3[3]; 

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

... 

int_array3& ref = a.foo(); 
+1

'auto & ref = a.foo();' также возможно –

11

Вы должны использовать std::array, чтобы избежать путаницы и сделать чище, безопаснее и менее неуклюжим код:

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; 
} 

Я знаю, что имя массива является указателем на первый элемент в этом массиве

Это неверно, массив может быть неявно преобразован в указатель на первый элемент. Это не то же самое.

+1

Это довольно вводит в заблуждение, потому что создается впечатление, что ссылка на массив не может быть возвращена или что возвращаемое значение не может быть использовано , – juanchopanza

+2

@juanchopanza Где я это сказал? – Slava

+1

Я не имел в виду, что вы это говорите. Просто, сосредоточившись на решении 'std :: array', не упоминая, что он идеально подходит для возврата и использует ссылку на массив, создается впечатление, что вы не можете этого сделать. – juanchopanza

0

Некоторые альтернативы, я думаю, выше в C++ 14 и далее:

авто и ссылки на массив отставая тип возвращаемого значения:

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

decltype (авто):

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

Пример использования:

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); 
0

В условиях компиляции этот вопрос очень важен.Рассмотрим эту функцию constexpr:

template<typename T, std::size_t N, 
    typename outype = unsigned(&)[N] > 
inline constexpr outype && indexer(T(&incoming)[N]) 
{ 
    unsigned indexes[N]{ }; 
    for (size_t j = 0; j != N; ++j) indexes[j] = j; 
    return std::move(indexes); 
} 

Использование требует decltype (авто) идиомы:

struct X {}; 
    constexpr X decisions[]{ X{}, X{}, X{}, X{} }; 
    decltype(auto) indices = indexer(decisions); 

Без decltype (авто), мы позволили бы распада массива в указатель. Выше компилируется с msvc (последний по состоянию на 2018MAR05). Но не с последним GCC (C++ 2a) ...

Смежные вопросы