Output dari program ini:
#include <iostream>
class c1
{
public:
c1& meth1(int* ar) {
std::cout << "method 1" << std::endl;
*ar = 1;
return *this;
}
void meth2(int ar)
{
std::cout << "method 2:"<< ar << std::endl;
}
};
int main()
{
c1 c;
int nu = 0;
c.meth1(&nu).meth2(nu);
}
Adalah:
method 1
method 2:0
Mengapa nu
bukan 1 saat meth2()
dimulai?
c++
chaining
operator-precedence
Moises Viñas
sumber
sumber
nu
,,&nu
danc
ke tumpukan dalam urutan itu, kemudian memanggilmeth1
, mendorong hasil ke stack, kemudian memanggilmeth2
, sementara konvensi pemanggilan berbasis register ingin memuatc
dan&nu
ke register, memanggilmeth1
, memuatnu
ke register, lalu memanggilmeth2
.Jawaban:
Karena urutan evaluasi tidak ditentukan.
Anda melihat
nu
dimain
sedang dievaluasi untuk0
bahkan sebelummeth1
disebut. Ini adalah masalah dengan rantai. Saya menyarankan untuk tidak melakukannya.Buat saja program yang bagus, sederhana, jelas, mudah dibaca, dan mudah dipahami:
sumber
<<
untuk keluaran, dan "pembangun objek" untuk objek kompleks dengan terlalu banyak argumen ke konstruktor - tetapi sangat buruk bercampur dengan argumen keluaran.meth1
danmeth2
ditentukan, tetapi evaluasi parameter untukmeth2
mungkin terjadi sebelummeth1
disebut ...?meth2(meth1(c, &nu), nu)
Menurut saya, bagian dari draf standar mengenai urutan evaluasi ini relevan:
dan juga:
Jadi untuk baris Anda
c.meth1(&nu).meth2(nu);
, pertimbangkan apa yang terjadi di operator dalam hal operator pemanggilan fungsi untuk panggilan terakhir kemeth2
, jadi kita dengan jelas melihat perincian ke dalam ekspresi dan argumen postfixnu
:The evaluasi dari ekspresi postfix dan argumen untuk final panggilan fungsi (yaitu ekspresi postfix
c.meth1(&nu).meth2
dannu
) yang relatif unsequenced satu sama lain sesuai dengan fungsi panggilan aturan di atas. Oleh karena itu, efek samping dari penghitungan ekspresi postfix pada objek skalarar
tidak diurutkan secara relatif terhadap evaluasi argumennu
sebelummeth2
pemanggilan fungsi. Dengan aturan eksekusi program di atas, ini adalah perilaku yang tidak ditentukan.Dengan kata lain, tidak ada persyaratan bagi kompilator untuk mengevaluasi
nu
argumenmeth2
panggilan setelahmeth1
panggilan - itu bebas untuk mengasumsikan tidak ada efek samping darimeth1
mempengaruhinu
evaluasi.Kode assembly yang dihasilkan di atas berisi urutan
main
fungsi berikut:nu
dialokasikan pada stack dan diinisialisasi dengan 0.ebx
dalam kasus saya) menerima salinan nilainu
nu
danc
dimuat ke register parametermeth1
disebutnu
dalamebx
register dimuat ke register parametermeth2
disebutSecara kritis, pada langkah 5 di atas compiler memungkinkan nilai yang di-cache
nu
dari langkah 2 untuk digunakan kembali dalam pemanggilan fungsi kemeth2
. Di sini mengabaikan kemungkinan yangnu
mungkin telah diubah oleh ajakan untukmeth1
- 'perilaku yang tidak ditentukan' dalam tindakan.CATATAN: Jawaban ini telah berubah substansi dari bentuk aslinya. Penjelasan awal saya dalam hal efek samping komputasi operan tidak diurutkan sebelum pemanggilan fungsi terakhir tidak benar, karena memang demikian. Masalahnya adalah fakta bahwa penghitungan operan itu sendiri diurutkan secara tidak pasti.
sumber
meth1
tersebut dijalankan sebelumnyameth2
, tetapi parameter untukmeth2
adalah nilai yang di-nu
cache ke dalam register sebelum panggilan kemeth1
- yaitu kompilator telah mengabaikan potensi efek sampingnya, yaitu konsisten dengan jawaban saya.c.meth1(&nu).meth2
) dan evaluasi argumen ke panggilan tersebut (nu
) umumnya tidak diurutkan, tetapi 1) efek sampingnya semua diurutkan sebelum masuk kemeth2
dan 2) karenac.meth1(&nu)
adalah panggilan fungsi , itu diurutkan secara tidak pasti dengan evaluasinu
. Di dalammeth2
, jika entah bagaimana memperoleh pointer ke variabel dimain
, itu akan selalu melihat 1.meth2
, seperti yang disebutkan dalam item 3 dari halaman cppreference yang Anda kutip (yang juga Anda lalai untuk mengutip dengan benar).Dalam standar 1998 C ++, Bagian 5, paragraf 4
(Saya telah menghilangkan referensi ke catatan kaki # 53 yang tidak relevan dengan pertanyaan ini).
Pada dasarnya,
&nu
harus dievaluasi sebelum meneleponc1::meth1()
, dannu
harus dievaluasi sebelum meneleponc1::meth2()
. Namun, tidak ada persyaratan yangnu
dievaluasi sebelumnya&nu
(misalnya, diizinkannu
untuk dievaluasi terlebih dahulu, kemudian&nu
, dan kemudianc1::meth1()
dipanggil - yang mungkin dilakukan oleh compiler Anda). Oleh karena itu, ekspresi*ar = 1
dalamc1::meth1()
tidak dijamin akan dievaluasi sebelumnu
inmain()
dievaluasi, untuk diteruskan kec1::meth2()
.Standar C ++ kemudian (yang saat ini tidak saya miliki di PC yang saya gunakan malam ini) pada dasarnya memiliki klausa yang sama.
sumber
Saya pikir ketika menyusun, sebelum fungsi meth1 dan meth2 benar-benar dipanggil, parameter telah diteruskan ke mereka. Maksud saya saat Anda menggunakan "c.meth1 (& nu) .meth2 (nu);" nilai nu = 0 telah diteruskan ke meth2, jadi tidak masalah apakah "nu" diubah nantinya.
Anda bisa mencoba ini:
itu akan mendapatkan jawaban yang Anda inginkan
sumber