2014-11-07 18 views
8

Aşağıdaki kod yasal mı (C++ 11 ve/veya C++ 14 standart (lar))?Reinterpret_cast (veya herhangi bir oyuncu) xvalues'ı lvalues'e dönüştürebilir mi?

#include <iostream> 
#include <utility> 

using namespace std; 

void foo(int &a) { 
    cout << a << endl; 
} 

int main() { 
    foo(reinterpret_cast<int &>(move(5))); 
} 
  • evet, tanımsız davranış ise?
  • Tanımlanmamış bir davranış değilse, a'u foo içinde UB olmaksızın bile değiştirebilir miyim?

Bu gcc 4.9 üzerinde değil, clang 3.5 üzerinde derler. GCC hatası: Her iki GCC ve Clang için C++, 11 üzerinde çalışır

➤ g++-4.9 -std=c++1y sample.cpp -o sample                       
sample.cpp: In function 'int main()': 
sample.cpp:11:40: error: invalid cast of an rvalue expression of type 'std::remove_reference<int>::type {aka int}' to type 'int&' 
    foo(reinterpret_cast<int &>(move(5))); 
             ^

Bilginize DÜZENLEME, önceki daha az kıllı bir özel yapılmış döküm ve aşağıdaki lvalue fonksiyonu olacaktır:

#include <iostream> 

namespace non_std { 
    template <typename T> 
    constexpr T &lvalue(T &&r) noexcept { return r; } 
} 

void divs(int &a, int &b) { 
    int t = a; 
    a /= b; 
    b /= t; 
} 

int main() { 
    using namespace std; 
    using namespace non_std; 

    int i_care_for_this_one = 4; 
    divs(i_care_for_this_one, lvalue(2)); 
    cout << i_care_for_this_one << endl; 
} 
+0

neden standart uyumlu derleyiciyi kullanarak derlemeye ve bundan sonra ne olacağını görmeye çalışmıyorsunuz? standart uyumlu derleyici tarafından – Raptor

+0

@Raptor ben standart kendisi yoruma açık olduğu zaman, hatasız tanımlanmamış derleme davranışına onu açık uygulayacağını ideal kimse. –

+1

GCC'de aldığınız hata mesajından bahsedebilirsiniz. Visual Studio 2013, "C2102 hatası:" ve "l değeri" gerektirir. –

cevap

6

Güncelleme: Kod bozuk C++ 11 biçimindedir. Aşağıdaki cevap C++ 14 içindir. Bu cevabın sonunda not bakın.

bu kod hem iyi oluşturulmuş ve iyi tanımlanmış olduğuna inanıyoruz. İşte nedeni.

std::move sonucu, bir değer olan bir xvalue [1] olup; ve reinterpret_cast bir lvalue referansa bir glvalue dönüştürülmesi standart ifadeler izin gibi görünmektedir: "int işaretçi" "int işaretçi" dönüştürülebilir

A glvalue expression of type T1 can be cast to the type “reference to T2 ” if an expression of type “pointer to T1 ” can be explicitly converted to the type “pointer to T2 ” using a reinterpret_cast . The result refers to the same object as the source glvalue, but with the specified type. [ Note: That is, for lvalues, a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)). — end note ] No temporary is created, no copy is made, and constructors (12.1) or conversion functions (12.3) are not called.73

için, bu reinterpret_cast kullanmak da mümkündür. Standart, hedef tipin bir referans referans mı yoksa referans referans mı olduğu konusunda bir şey söylemez.

Dökümün sonucu, yukarıdaki paragrafa göre iyi tanımlanmıştır: kaynak glilue ile aynı nesneyi ifade eder; yani 5 değerine sahip geçici bir int nesnesi. ([dcl.init.ref], bir referansın bir referansa bağlı olması durumunda geçici olarak oluşturulduğunu belirtir.)

int& aracılığıyla değere erişme, orijinal nesne de türden olduğu için, herhangi bir takma kuralını da ihlal etmez. int. Aslında, geçici olarak bu şekilde elde edilen değer boyunca değişiklik yapmak için iyi tanımlanabileceğine inanıyorum.

Not: C++ 11 ifadesi "glilue ifadesi" değil "lvalue expression" yazıyor. "Glentue ifadesi" ifadesi, C++ 14 için son çalışma taslağı olan N3936'dan alınmıştır. Ben standardizasyon işleminin nasıl bir uzman değilim, ama bu, ISO C++ 14 standardına yayınladığında "lvalue" nin değişiklik zaten komite tarafından oy kullandı edildi ve "glvalue", gidiyor anlamına gelir inanıyoruz Yukarıda söylediklerine oldukça benzer.

[1] Argümanın bir işlev olduğu nadir durumlar hariç; Bu durumda, sonuç hiçbir fonksiyon rölesi olmadığı için bir değerdir.

+0

Sanırım cevabınız, hepsine karşı bir karşı argüman gelene kadar doğru olanıdır. –

+1

@hs_ yanıtındaki ifade, C++ 11 standardındaki gibi görünüyor (doğruluğunu kontrol edemiyorum). Bahsettiğiniz ifadeler [github taslağındadır] (https://github.com/cplusplus/draft/blob/master/source/expressions.tex#L2281), emin değilim ki C++ ile aynıdır. 14 standart. Bu konuda kafam karıştı ve C++ 14'te C++ 11'de kodun yasal olup olmadığı bende. (clang hala C++ 11 modunda derler) –

+0

Teşekkürler, Bu cevabı kabul edeceğim. Seçimi sadece birisinin bana C++ 14 son final ISO'nun sihirli bir şekilde değiştiğini söylemesi durumunda bırakacağım. –

1

Standartla ilgili bölüm 5.2.10 [expr.reinterpret.cast] 'dir. İki ilgili paragraflar vardır:

Öncelikle biter paragraf 1 vardır:

No other conversion can be performed explicitly using reinterpret_cast.

... ve diğer hiçbirinin paragraf 11 geçerlidir:

A glvalue expression of type T1 can be cast to the type “reference to T2 ” if an expression of type “pointer to T1 ” can be explicitly converted to the type “pointer to T2 ” using a reinterpret_cast . The result refers to the same object as the source glvalue, but with the specified type. [ Note: That is, for lvalues, a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators (and similarly for reinterpret_cast<T&&>(x)) . —end note ] No temporary is created, no copy is made, and constructors (12.1) or conversion functions (12.3) are not called.

Diğer tüm cümleleri nesneler için değil, yalnızca işaretçiler, işlevler için işaretçiler vb. için geçerli değildir. Bir rengin bir glival olmadığı için, kod geçersizdir.

+0

Brian'ın işaret ettiği gibi, 'std :: move' sonucu bir xvalue (bir değer); [basic.lval/1] "Dönüş türü bir rvalue başvurusu olan bir işlevi çağırmanın sonucu bir xvalue'dir." ve "std :: move" tek argümanı sürümü –

+0

@MattMcNabb işlevidir, 'std: : move 'orada amaç, clang onsuz bir hata sağlar (bu nedenle soru). –

+0

Görebildiğim kadarıyla kod, 'move' → * xvalue * → * glvalue * nedeniyle geçerli olmalıdır. Std :: remove_reference' (sans reference) nereden geldiğini merak ediyorum. –

5

Sorun, reinterpret_cast'in xvalues ​​değerini lvalues'a dönüştürmesine izin verilip verilmediğiyle ilgilidir. Aksine diğerleri yapıştırırken ne kadar, ilgili paragraf (5.2.10.11) sadece SolDeğerler bahseder: glvalue için ifadeleri değiştirmek için Michael Wong tarafından sunulan bir öneri yoktur

An lvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast..

, ama bir duvara çarpmış gibi görünüyor:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1268

Ben dönüşüm yasal değildir şimdi olarak sadece açıkça SolDeğerler dönüşümü sağlar beri bu, olarak kabul ediyorum.

+0

Thx! –

İlgili konular