Apa arti "int & foo ()" di C ++?

114

Saat membaca penjelasan tentang lvalues ​​dan rvalues ​​ini, baris kode berikut menarik perhatian saya:

int& foo();
foo() = 42; // OK, foo() is an lvalue

Saya mencobanya di g ++, tetapi kompilernya mengatakan "referensi tidak terdefinisi ke foo ()". Jika saya menambahkan

int foo()
{
  return 2;
}

int main()
{
  int& foo();
  foo() = 42;
}

Ini mengkompilasi dengan baik, tetapi menjalankannya memberikan kesalahan segmentasi . Hanya garisnya

int& foo();

dengan sendirinya mengkompilasi dan berjalan tanpa masalah.

Apa arti kode ini? Bagaimana Anda bisa menetapkan nilai ke panggilan fungsi, dan mengapa itu bukan nilai r?

Sossisos
sumber
25
Anda tidak menetapkan nilai ke panggilan fungsi , Anda sedang menetapkan nilai ke referensi yang dikembalikannya .
Frédéric Hamidi
3
@ FrédéricHamidi menetapkan nilai ke objek yang merujuk ke referensi yang dikembalikan
MM
3
Ya itu alias untuk objek; referensi bukan objek
MM
2
Deklarasi fungsi lokal @RoyFalk adalah cacar, secara pribadi saya akan senang melihatnya dihapus dari bahasa. (Tidak termasuk lambda tentu saja)
MM
4
Sudah ada bug GCC untuk ini .
jotik

Jawaban:

168

Penjelasannya mengasumsikan bahwa ada beberapa implementasi yang masuk akal fooyang mengembalikan referensi nilai l ke valid int.

Implementasi seperti itu mungkin:

int a = 2; //global variable, lives until program termination

int& foo() {
    return a;
} 

Sekarang, karena foomengembalikan referensi lvalue, kita dapat menetapkan sesuatu ke nilai kembalian, seperti:

foo() = 42;

Ini akan memperbarui global adengan nilai 42, yang dapat kita periksa dengan mengakses variabel secara langsung atau memanggil foolagi:

int main() {
    foo() = 42;
    std::cout << a;     //prints 42
    std::cout << foo(); //also prints 42
}
TartanLlama
sumber
Masuk akal seperti pada operator []? Atau metode akses anggota lainnya?
Jan Dorniak
8
Mengingat kebingungan tentang referensi, mungkin yang terbaik adalah menghapus variabel lokal statis dari sampel. Inisialisasi menambah kerumitan yang tidak perlu, yang merupakan rintangan untuk belajar. Jadikan saja global.
Mooing Duck
72

Semua jawaban lainnya menyatakan statis di dalam fungsi. Saya pikir itu mungkin membingungkan Anda, jadi lihat ini:

int& highest(int  & i, int  & j)
{
    if (i > j)
    {
        return i;
    }
    return j;
}

int main()
{
    int a{ 3};
    int b{ 4 };
    highest(a, b) = 11;
    return 0;
}

Karena highest()mengembalikan referensi, Anda dapat menetapkan nilai padanya. Ketika ini berjalan, bakan berubah menjadi 11. Jika Anda mengubah inisialisasi sehingga a, katakanlah, 8, maka aakan berubah menjadi 11. Ini adalah beberapa kode yang mungkin benar-benar memiliki tujuan, tidak seperti contoh lainnya.

Kate Gregory
sumber
5
Apakah ada alasan untuk menulis int a {3} daripada int a = 3? Sintaks ini tampaknya tidak sesuai bagi saya.
Aleksei Petrenko
6
@AlexPetrenko itulah inisialisasi universal. Saya kebetulan menggunakannya dalam contoh yang saya miliki. Tidak ada yang tidak pantas tentang itu
Kate Gregory
3
Aku tahu ini apa. a = 3 terasa jauh lebih bersih. Preferensi pribadi)
Aleksei Petrenko
Sama seperti mendeklarasikan statis di dalam suatu fungsi yang mungkin membingungkan sebagian orang, saya juga percaya menggunakan inisialisasi universal kemungkinan besar akan membingungkan.
ericcurtin
@AlekseiPetrenko Dalam kasus int a = 1.3 secara implisit diubah menjadi a = 1. Sedangkan int a {1.3} menghasilkan kesalahan: mempersempit konversi.
Sathvik
32
int& foo();

Mendeklarasikan fungsi bernama foo yang mengembalikan referensi ke file int. Contoh yang gagal dilakukan adalah memberikan definisi fungsi yang dapat Anda kompilasi. Jika kami menggunakan

int & foo()
{
    static int bar = 0;
    return bar;
}

Sekarang kita memiliki fungsi yang mengembalikan referensi bar. karena bilah staticitu akan hidup setelah panggilan ke fungsi sehingga mengembalikan referensi ke sana aman. Sekarang jika kita melakukannya

foo() = 42;

Apa yang terjadi adalah kita menetapkan 42 barkarena kita menetapkan ke referensi dan referensi tersebut hanyalah alias untuk bar. Kalau kita panggil lagi fungsinya seperti

std::cout << foo();

Ini akan mencetak 42 karena kita mengaturnya di baratas.

NathanOliver
sumber
15

int &foo();mendeklarasikan fungsi yang dipanggil foo()dengan tipe returnint& . Jika Anda memanggil fungsi ini tanpa menyediakan isi maka kemungkinan besar Anda akan mendapatkan kesalahan referensi yang tidak ditentukan.

Dalam upaya kedua Anda, Anda memberikan fungsi int foo(). Ini memiliki tipe pengembalian yang berbeda dengan fungsi yang dideklarasikan oleh int& foo();. Jadi Anda memiliki dua deklarasi yang sama fooyang tidak cocok, yang melanggar Aturan Satu Definisi yang menyebabkan perilaku tidak terdefinisi (tidak diperlukan diagnostik).

Untuk sesuatu yang berhasil, keluarkan deklarasi fungsi lokal. Mereka dapat menyebabkan perilaku diam yang tidak terdefinisi seperti yang Anda lihat. Sebagai gantinya, gunakan hanya deklarasi fungsi di luar fungsi apa pun. Program Anda akan terlihat seperti:

int &foo()
{
    static int i = 2;
    return i;
}  

int main()
{
    ++foo();  
    std::cout << foo() << '\n';
}
MM
sumber
10

int& foo();adalah fungsi yang mengembalikan referensi ke int. Fungsi yang Anda berikan kembali inttanpa referensi.

Anda boleh melakukannya

int& foo()
{
    static int i = 42;
    return i;
}

int main()
{
    int& foo();
    foo() = 42;
}
Jarod42
sumber
7

int & foo();artinya foo()mengembalikan referensi ke variabel.

Pertimbangkan kode ini:

#include <iostream>
int k = 0;

int &foo()
{
    return k;
}

int main(int argc,char **argv)
{
    k = 4;
    foo() = 5;
    std::cout << "k=" << k << "\n";
    return 0;
}

Kode ini mencetak:

$ ./a.out k = 5

Karena foo()mengembalikan referensi ke variabel global k.

Dalam kode yang direvisi, Anda mentransmisikan nilai yang dikembalikan ke referensi, yang kemudian tidak valid.

vy32
sumber
5

Dalam konteks itu & berarti referensi - jadi foo mengembalikan referensi ke int, bukan int.

Saya tidak yakin apakah Anda telah bekerja dengan pointer, tapi itu ide yang sama, Anda tidak benar-benar mengembalikan nilai dari fungsinya - sebagai gantinya Anda meneruskan informasi yang diperlukan untuk menemukan lokasi dalam memori di mana itu int adalah.

Jadi untuk meringkas Anda tidak menetapkan nilai ke panggilan fungsi - Anda menggunakan fungsi untuk mendapatkan referensi, lalu menetapkan nilai yang direferensikan ke nilai baru. Sangat mudah untuk berpikir bahwa semuanya terjadi sekaligus, tetapi pada kenyataannya komputer melakukan semuanya dengan urutan yang tepat.

Jika Anda bertanya-tanya - alasan Anda mendapatkan segfault adalah karena Anda mengembalikan literal numerik '2' - jadi itu adalah kesalahan tepat yang akan Anda dapatkan jika Anda mendefinisikan sebuah const int dan kemudian mencoba untuk memodifikasi nilai.

Jika Anda belum belajar tentang pointer dan memori dinamis, maka saya akan merekomendasikannya terlebih dahulu karena ada beberapa konsep yang menurut saya sulit untuk dipahami kecuali Anda mempelajarinya sekaligus.

Dar Brett
sumber
4

Kode contoh di halaman yang ditautkan hanyalah deklarasi fungsi tiruan. Ini tidak dapat dikompilasi, tetapi jika Anda memiliki beberapa fungsi yang ditentukan, ini akan bekerja secara umum. Contoh tersebut berarti "Jika Anda memiliki fungsi dengan tanda tangan ini, Anda dapat menggunakannya seperti itu".

Dalam contoh Anda, foojelas mengembalikan nilai l berdasarkan tanda tangan, tetapi Anda mengembalikan nilai r yang diubah menjadi nilai l. Ini jelas ditentukan untuk gagal. Anda bisa melakukan:

int& foo()
{
    static int x;
    return x;
}

dan akan berhasil dengan mengubah nilai x, ketika mengatakan:

foo() = 10;
Es api
sumber
3

Fungsi yang Anda miliki, foo (), adalah fungsi yang mengembalikan referensi ke integer.

Jadi katakanlah aslinya foo dikembalikan 5, dan kemudian, di fungsi utama Anda, Anda katakan foo() = 10; , lalu mencetak foo, itu akan mencetak 10 bukan 5.

Saya harap itu masuk akal :)

Saya baru mengenal pemrograman juga. Sangat menarik melihat pertanyaan seperti ini yang membuat Anda berpikir! :)

psj01
sumber