C ++ передає масив за посиланням


78

чи дозволено це передавати масив за посиланням?

 void foo(double& *bar) 

Здається, мій компілятор каже ні. Чому? Який правильний спосіб передавати масив за посиланням? Або попрацювати? У мене є аргумент масиву, який мій метод повинен змінити, і який я повинен отримати потім. Як варіант, я міг би зробити цей масив членом класу, який чудово працює, але він має багато недоліків для іншої частини мого коду (чого я хотів би уникнути).

Дякую і вітаю.



1
Ви розглядали можливість використання std::vectorабо подібного?
moooeeeep,

1
@moooeeeep std::vector, очевидно, є вищим, якщо розмір динамічний / невідомий, але у випадках, коли розмір завжди однаковий, він буде надмірним, і замість цього std::arrayвирішує синтаксичну потворність, одночасно надаючи значення семантики.
underscore_d


чому не просто void foo (подвійна смуга [])? якщо ви хочете, ви також можете вказати довжину масиву як void foo (подвійний стовпчик [2]). Хіба що для складного типу потрібно уникати конструктора копіювання.
остаточна причина

Відповіді:


136

Масиви можна передавати лише за посиланням, насправді:

void foo(double (&bar)[10])
{
}

Це заважає робити такі речі:

double arr[20];
foo(arr); // won't compile

Щоб мати можливість передавати масив довільного розміру foo, зробіть його шаблоном і захопіть розмір масиву під час компіляції:

template<typename T, size_t N>
void foo(T (&bar)[N])
{
    // use N here
}

Ви повинні серйозно розглянути питання про використання std::vector, або якщо у вас є компілятор , який підтримує C ++ 11, std::array.


7
Msgstr "Однак, після передачі fooмасиву масив перетворюється на покажчик". Ні, це не так, і оскільки ви можете передавати лише масив з 10 елементів, вам не потрібна додаткова інформація про розмір. (І ще раз, ви не можете передати масив void foo( double*& ); результати неявного перетворення є значенням r і не можуть бути використані для ініціалізації посилання на non-const. Ви повинні використовувати void foo( double *const & );.
James Kanze,

1
чудово :) це саме те, що я шукаю. Я робив це void foo(T &bar[N])у своєму шаблоні - але це насправді означає "масив посилань", а не "посилання на масив".
OLL

Якщо така функція вже існує, і все, що у вас є - це std::vector(правильного розміру), як би ви перекинули вміст вектора (можливо, отриманого через std::vector::data()) до цього типу масиву? Який синтаксис фіксованого розміру масиву в параметрі типу (тобто для використання з ним static_cast<>)?
davidA

1
@meowsqueak Ти б цього не зробив, тому що акторський склад мав би невизначену поведінку, AFAICT; Я не бачу жодного дозволу reinterpret_castна обробку динамічно розподіленого масиву (без внутрішнього розміру після виділення) як автоматичного з фіксованим розміром. Крім того, це неправильне запитання: правильним буде запитання: "Як я можу рефакторингувати свій код для роботи з обома типами контейнерів?", А відповідь - шаблони та ітератори / діапазони, або оскільки ви сказали, що розмір фіксований, функцією що приймає лише покажчик даних / посилання та жорсткі коди розміру (або, якщо розмір може змінюватися, наприклад, адаптер даних / розміру span)
underscore_d

чому не просто void foo (подвійна смуга [])? якщо ви хочете, ви також можете вказати довжину масиву як void foo (подвійний стовпчик [2]). Хіба що для складного типу потрібно уникати конструктора копіювання.
остаточна причина

18

Так, але при збігу аргументів для посилання неявний масив покажчика не є автоматичним, тому вам потрібно щось на зразок:

void foo( double (&array)[42] );

або

void foo( double (&array)[] );

Однак майте на увазі, що при збігу double [42]і double []існують різні типи. Якщо у вас є масив невідомого виміру, він буде відповідати другому, але не першому, а якщо у вас масив з 42 елементами, він буде відповідати першому, але не другому . (Останнє, IMHO, дуже протилежне інтуїції.)

У другому випадку вам також доведеться передати розмірність, оскільки немає можливості відновити його, коли ви перебуваєте всередині функції.


7
Другий не складається для мене.
jrok

Для мене ні. Я не знаю, чи це пов’язано з помилкою компілятора чи чимось із того стандарту, який я пропустив. Я ніколи насправді не хотів цього робити. (Перший дуже корисний, якщо розмір є параметром шаблону, оскільки компілятор визначить правильний розмір для вас.)
Джеймс Канзе,

6
Я не вважаю, що другий приклад є дійсним, що відповідає стандарту C ++. До речі, він не компілюється з GCC 4.8 або Clang 3.4. Я думаю, що шаблонний розмір необхідний.
Ricky65

2
@ Ricky65 Так воно і є. Цікаво, чому. Посилання на неповні типи зазвичай дозволяються як параметри функції. (Звичайно, у цьому випадку тип змінюється після його завершення: double []і double [42]це два різні типи. Отже, єдиним, що ви можете передати до останньої форми, буде масив невідомої довжини, наприклад extern double d[];, або якийсь такий. Що серйозно обмежує корисність.)
Джеймс Канце,

6

Якщо ви хочете змінити лише елементи:

void foo(double *bar);

достатньо.

Якщо ви хочете змінити адресу на (наприклад:) realloc, але вона не працює для масивів:

void foo(double *&bar);

є правильною формою.


4
За винятком того, що ви не можете передати масив останньому. Він запитував про масиви, а не вказівники.
James Kanze,

@JamesKanze: правда. Другий випадок не буде працювати з масивами у стеку, в такому випадку він повинен використовувати першу форму.
Нашта

8
Другий випадок не буде працювати для масивів, точка. Це буде працювати, лише якщо у вас є фактична змінна типу вказівника.
James Kanze,

6

Оскільки ви використовуєте С ++, обов’язковою пропозицією, якої тут ще немає, є використання std::vector<double>.

Ви можете легко передати його за посиланням:

void foo(std::vector<double>& bar) {}

А якщо у вас є підтримка C ++ 11, також подивіться std::array.

Для довідки:


5
:-) Так. std::vectorзазвичай є найкращим рішенням. Але є винятки.
James Kanze,

3

8.3.5.8 Якщо тип параметра включає тип форми "вказівник на масив невідомої межі T" або "посилання на масив невідомої межі T", програма неправильно сформована


2

Як говорить інша відповідь, поставте &після *.

Це піднімає цікавий момент, який іноді може заплутати: типи слід читати справа наліво. Наприклад, це (починаючи з самого праворуч *) вказівник на постійний вказівник на int.

int * const *x;

Отже, написане вами буде вказівником на посилання, що неможливо.


Зміна порядку все ще не дозволяє йому передавати масив. Для цього потрібен фактичний об’єкт покажчика, а не результати перетворення.
James Kanze,

1

Тут Ерік пояснює всі способи передачі масиву посиланням : https://stackoverflow.com/a/5724184/5090928 .

Аналогічним чином ви можете створити посилальну змінну масиву приблизно так:

int arr1[] = {1, 2, 3, 4, 5};
int(&arr2)[5] = arr1;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.