Функция 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
квалификатор опущен в коде.
я знаю, что имя массива является указателем на первый элемент в этом массиве, так что использование указателя для получения абсолютно жизнеспособен.
Извините, я ошибся. От массив распад указателя
существует неявное преобразование из lvalues и rvalues типа массива в rvalues типа указателя: он создает указатель на первый элемент массива.
Пожалуйста, игнорируйте это XD
мне просто интересно, о чем я спросил в начале:)
4 ответов
есть ли другой способ получить ссылку на массив из возвращаемой функции, кроме использования указателя?
Да, используя ссылку на массив, как и с любого другого типа:
int (&ref)[3] = a.foo();
чтобы избежать неуклюжего синтаксиса, вы можете использовать typedef
вместо.
typedef int int_array3[3];
...
int_array3& foo() { return ia; }
...
int_array3& ref = a.foo();
вы должны использовать 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;
}
Я знаю, что имя массива является указателем на первый элемент в массиве
Это неверно, массив может быть неявно преобразован в указатель на первый элемент. Это не одно и то же.
некоторые альтернативы, которые я думаю, превосходят в C++14:
auto и array reference trailing тип возврата:
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);
чтобы ответить на этот вопрос, можно разработать решение вокруг std::reference_wrapper. Этот код компилируется и запускается.
#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);
}
}