2014-09-24 14 views
11

Nieco zaskakująco (do mnie), następujące dwa programy kompilować do różnych wyjść, z tym drugim związkiem mającym znacznie lepsze osiągi (testowane z gcc i brzękiem):Automatyczne xvalue optymalizacja

#include <vector> 
int main() 
{ 
    std::vector<int> a(2<<20); 
    for(std::size_t i = 0; i != 1000; ++i) { 
     std::vector<int> b(2<<20); 
     a = b; 
    } 
} 

vs.

#include <vector> 
int main() 
{ 
    std::vector<int> a(2<<20); 
    for(std::size_t i = 0; i != 1000; ++i) { 
     std::vector<int> b(2<<20); 
     a = std::move(b); 
    } 
} 

Czy ktoś mógłby mi wyjaśnić, dlaczego kompilator robi (lub nie może) automatycznie rozważyć b xvalue w ostatnim zadaniu i zastosować semantykę ruchu bez jawnego rzutowania std::move?

Edit: Zestawione z (g++|clang++) -std=c++11 -O3 -o test test.cpp

+0

Jakie parametry przekazujesz kompilatorom? – Joe

+1

Moim pierwszym przypuszczeniem jest to, że zmieniłoby to semantykę programu w nieoczekiwany sposób, zamieniając kopię w ruch. – pmr

+0

@pmr: To też podejrzewam, ale naprawdę chciałbym zrozumieć dlaczego. Naiwnie, wygląda dokładnie tak, jak powinna być dla mnie wartość. – Xoph

Odpowiedz

6

Compilers can't break the as-if rule

Jako §1.9/1 stanowi:

semantyczne opisy w niniejszej normie międzynarodowej zdefiniować parametryczne niedeterministycznych abstrakcyjnej maszyny. Ten międzynarodowy standard nie nakłada wymagań na strukturę zgodnych implementacji. W szczególności nie muszą kopiować ani emulować struktury abstrakcyjnej maszyny. Przeciwnie, zgodne implementacje muszą współzawodniczyć (tylko) zaobserwowania zachowania abstrakcyjnej urządzenia jak opisano poniżej

to kompilator nie może zmienić zaobserwowania zachowania programu. Automatycznie (nawet bez żadnych reperkusji) konwersja przypisania do przypisania przeniesienia przerwałaby to stwierdzenie.

Kopiowanie elekcje może nieznacznie zmienić to zachowanie, ale jest to regulowane przez § 12.8/31.

Jeśli chcesz użyć wersji przeniesienia, musisz jawnie poprosić o nią, tak jak w tym ostatnim przykładzie.

+1

OK, więc w szczególności programista powinien mieć niezawodne wywoływanie kopiowania/przenoszenia operatora/ctor. Jakoś założyłem, że rozsądnie byłoby wymagać od programisty, aby te operacje były zgodne semantycznie. Sądzę, że oba podejścia miałyby swoje wady i zalety, ale widzę, dlaczego standard widzi to inaczej. – Xoph

+2

W tym konkretnym kodzie nie złamie reguły "jak gdyby", ponieważ nie ma wyjścia –

5

Spójrzmy na kolejnej próbki (proszę ignorować void typ zwracany z operator=):

#include <iostream> 

struct helper 
{ 
    void operator=(helper&&){std::cout<<"move"<<std::endl;} 
    void operator=(const helper&){std::cout<<"copy"<<std::endl;} 
}; 

void fun() 
{ 
    helper a; 
    { 
     helper b; 
     a = b; 
    } 
} 

void gun() 
{ 
    helper a; 
    { 
     helper b; 
     a = std::move(b); 
    } 
} 
int main() 
{ 
    fun(); 
    gun(); 
} 

operator= ma różne zachowanie w zależności od jego argumentów. Kompilator może zoptymalizować kod tylko wtedy, gdy jest w stanie utrzymać obserwowalne zachowanie tak samo.

Zważywszy b z funxvalue, podczas gdy nie jest to xvalue w momencie wywołania Spowoduje to zmianę zaobserwowania zachowanie programu, a to nie jest pożądane ani dopuszczone przez standard.

+0

Dzięki, wiedziałem o zmianie w wywołaniu konstruktora. Zakładałem, że programista był w jakiś sposób zmuszony do przeniesienia konstruktorów/operatorów i kopiowania konstruktorów/operatorów "seantycznie pasujących" i myślę, że to moja wada tutaj! – Xoph