C++11 tyyppisälää jyke.jokinen@tut.fi, Ohjelmistotekniikka c Jyke Jokinen 2012 - http://creativecommons.org/licenses/by-nd-nc/1.0/fi Vakiokokoiset kokonaislukutyypit Aiemmin: Max ja min -arvot saatavissa numeric limits:n avulla. numeric_limits<int>::max() numeric_limits<int>::is_signed numeric_limits<int>::digits long long int It is guaranteed to be at least as large as a long int, and have no fewer than 64 bits cstdint (C99 standardi) intxx t signed integer type with width of exactly 8, 16, 32 and 64 bits respectively (provided only if the implementation directly supports the type) Laitteistonläheisessä koodauksessa saadaan täsmälleen haluttu koko luvulle (standardissa merkitty optional) int fastxx t fastest signed integer type with width of at least 8, 16, 32 and 64 bits respectively int leastxx t smallest signed integer type with width of at least 8, 16, 32 and 64 bits respectively 1
eräässä ympäristössä typedef signed char int8_t; typedef short int int16_t; typedef int int32_t; typedef long int int64_t; typedef signed char int_least8_t; typedef short int int_least16_t; typedef int int_least32_t; typedef long int int_least64_t; typedef signed char int_fast8_t; typedef long int int_fast16_t; typedef long int int_fast32_t; typedef long int int_fast64_t; lisäksi intmax t maximum width integer type intptr t integer type capable of holding a pointer kummallista std::string s1( false ); // compiles, calls char* constructor std::string s2( true ); // error void func(int n) { } void func(char* s) { } int main() { func( 0 ); func( NULL ); //??? } nullptr 1 void func(int n) { } 2 void func(char* s) { } 2
3 4 int main() { 5 func( 0 ); 6 func( nullptr ); 7 } nullptr on uusi avainsana kielessä. Tyypiltään pure rvalue std::nullptr t (pure ei voi olla viite). nullptr prvalue of this type is a null pointer constant and can be converted to a null pointer value or null pointer member pointer value. sizeof(std::nullptr t) shall be equal to sizeof(void*) osoitinta ja nullptr-arvoa voi verrata 1 char* ch = nullptr; 2 char* ch2 = 0; 3 int n = nullptr; // error 4 5 if( ch == nullptr ) // true 6 if( ch ) // false 7 if( nullptr ) // false 1 char* p1 = expr? 0 : nullptr; // error, types not compatible 2 const char* p2 = expr? nullptr : "merkkijono"; // ok 3 4 sizeof( nullptr ); // ok 5 typeid( nullptr ); // ok 6 throw nullptr; // ok 3
1 template<typename T1> void f(t1* t); 2 template<typename T2> void g(t2 t); 3 4 f( nullptr ); // error 5 f( (float*)nullptr ); // float* 6 7 g( 0 ); // deduces int 8 g( nullptr ); // deduces nullptr_t 9 g( (float*)nullptr ); // float* enum class 1 enum Color { RED, GREEN, BLUE }; 2 enum Feelings { ANGRY, MOODY, BLUE }; enum.cc:2:31: error: conflicting declaration BLUE enum.cc:1:33: error: BLUE has a previous declaration as Color BLUE 1 enum class Color { RED, GREEN, BLUE }; 2 enum class Feelings { ANGRY, MOODY, BLUE }; enum class Jokaisella enum class tyypillä oma nimiavaruus. Taaksepäin yhteesopivuus eli vanhat enum rakenteet toimivat. 1 Color color = Color::BLUE; 2 if( Feelings::BLUE == status ) { 3 //... 4 } 5 6 enum class Mood; 7 bool setstatus( Feelings f, Mood m ); enum class Halutessaan saa määritellä (tarkan) koon 4
1 enum class Colors : std::uint8_t 2 { RED = 1, GREEN = 2, BLUE = 3 }; 3 4 enum class ColorCode : char; // forward decl 5 void foobar( ColorCode* p ); enum class On olemassa myös enum struct. Ei semanttista eroa. Ei automaattista tyyppimuunnosta kokonaisluvuksi 1 int i = Colors::RED; // error: no Colors to int conversion 2 3 if( c == 3 ) {} 4 // error: no match for operator== in c == 3 5 6 if( static_cast<int>(c) == 3 ) {} // ok Ancient auto auto i = 42; Tilanvarausmääre auto, extern, static, register pick one Tyyppipäättely: auto 1 auto x = 10.0; 2 3 for (auto i = 0ul; i < v.size(); ++i); auto esimerkkejä 1 std::map<int,std::string> m; 2 auto i = m.begin(); 3 // -> std::map<int,std::string>::iterator 5
4 5 auto x1 = 10; 6 const auto* p = &x1; // const int* 7 const auto& r = m; 8 // -> const std::map<int,std::string>& 9 10 auto ci = m.cbegin(); 11 // -> std::map<int,std::string>::const_iterator const/volatile (cv) ja viite/osoitin -määreet voi lisätä auto ja template tyyppipäättely Lähtökohtana on tehdä tyyppipäättely samoin, miten template-mekanismissa on jo toteutettuna. 1 template<typename T> void f(t param); 2 3 f(expr); // deduce param s type from expr 4 auto v = expr; // do the same for v s type Kopioalustus ja suora alustus (rakentajasyntaksi) tarkoittavat samaa asiaa auto:n kannalta: 1 auto v1( expr ); // direct initialization 2 auto v2 = expr; // copy initialization auto 1 void f( std::string s ) { 2 auto tmp = s, *original = &s; 3 // std::string, std::string* 4 5 auto i = 10, d = 5.0; // error 6 // Each initialization must yield the same 7 // deduced type. 8 } 6
decltype semantiikka decltype(e) is the declared type of the name or expression E 1 const int& foo(); 2 int i; 3 struct A { double x; }; 4 const A* a = new A(); 5 6 decltype(i) x1; // type is int 7 decltype(foo()) x2; // type is const int& 8 decltype(a->x) x3; // type is double 9 decltype((a->x)) x4; // type is const double& 10 11 namespace std { typedef decltype(nullptr) nullptr_t; } decltype käytännössä 1 template<typename T, typename U> 2 void f(const vector<t>& a, vector<u>& b) 4 typedef decltype(a[0]*b[0]) Tmp; 5 for (int i=0; i<b.size(); ++i) { 6 Tmp* p = new Tmp(a[i]*b[i]); 7 //... 8 } 9 //... 10 } decltype käytännössä 1 template<class T, class U> 2??? mul(t x, U y) 4 return x*y; 5 } 1 template<class T, class U> 2 decltype(x*y) mul(t x, U y) // scope problem! 4 return x*y; 5 } 7
trailing return type 1 template<class T, class U> 2 decltype(*(t*)(0)**(u*)(0)) mul(t x, U y) 3 // calling that "not pretty" would be overly polite 4 { 5 return x*y; 6 } 1 template<class T, class U> 2 auto mul(t x, U y) -> decltype(x*y) 4 return x*y; 5 } auto ja decltype 1 const std::vector<int> v(1); 2 auto a = v[0]; // a has type int 3 decltype(v[0]) b = 1; // b has type const int&, the return type of 4 // std::vector<int>::operator[](size_type) const 5 auto c = 0; // c has type int 6 auto d = c; // d has type int 7 decltype(c) e; // e has type int, the type of the entity named c 8 decltype((c)) f = c;// f has type int&, because (c) is an lvalue 9 decltype(0) g; // g has type int, because 0 is an rvalue returntype käytännössä 1 class Item 2 { 3 }; 4 5 class Factory 6 { 7 static Item createobj( void ) { return Item(); } 8 }; 9 10 11 template <typename FactoryType, typename ReturnType> 12 ReturnType MakeInitObj( const FactoryType& factory ) 1 8
14 ReturnType item = factory.createobj(); 15 // item.init(); 16 return item; 17 } 1 template <typename FactoryType> 2 auto MakeInitObj( const FactoryType& factory ) 3 -> decltype( factory.createobj() ) 4 { 5 auto item = factory.createobj(); 6 // item.init(); 7 return item; 8 } auto is for everybody decltype is primarily for template authors Scott Meyers Arvonvälitys 1 template<typename T> 2 vector<t> doublevalues (const vector<t>& v) vector<t> retval( v.size() ); 4 for (auto itr = retval.begin(), 5 end_itr = retval.end(); 6 itr!= end_itr; ++itr ) 7 { retval.push_back( 2 * *itr ); } 8 9 return retval; 10 } 11 12 int main() 1 vector<int> v; 14 for ( int i = 0; i < 100; i++ ) 15 { v.push_back( i ); } 16 v = doublevalues<int>( v ); 17 } 9
rvalues string Name() { return "Jyke"; } int main() { const string& n1 = Name(); //string& n2 = Name(); string&& n3 = Name(); } rvaluen tunnistaminen void printref(const string& s) { cout << "lvalue" << endl; } void printref(string&& s) { cout << "rvalue" << endl; } int main() { string me( "Jyke" ); printref( me ); printref( Name() ); } lvalue vs rvalue lvalue on jotain mistä voi ottaa osoitinarvon. nimetty olio (tai pod) rvalue on jotain mistä ei saa otettu osoitetta. Tyypillisesti väliaikainen olio rvalue ref ja const rvalue-viite ei riko vakioita. rvalue- const lvalue/rvalue toimii ainoastaan const-viitteiden kanssa. viite ei muuta tätä. 10
kopioinnin välttäminen 1 1 class ArrayWrapper { 2 public: 3 ArrayWrapper(int n) : vals_( new int[n] ), size_(n) {}; 4 // copy constructor 5 ArrayWrapper(const ArrayWrapper& other) 6 : vals_( new int[ other.size_ ] ), 7 size_( other.size_ ) 8 { /* for... */ } 9 10 ~ArrayWrapper() 11 { delete [] vals_; } 12 private: 13 int* vals_; 14 int size_; 15 }; kopioinnin välttäminen 2 // move constructor ArrayWrapper(ArrayWrapper&& other) : vals_( other.vals_ ), size_( other.size_ ) { other.vals_ = nullptr; } // move assignment ArrayWrapper& operator=(arraywrapper&& other) { // if( this!= &other )... delete [] vals_; vals_ = other.vals_; size_ = other.size_; return *this; } rvalueviitteen ongelma(?) // move constructor ArrayWrapper(ArrayWrapper&& other) : vals_( other.vals_ ), size_( other.size_ ), meta_( other.meta_ ) 11
{ other.vals_ = nullptr; // saako käyttää other.meta_:aa? } // move constructor ArrayWrapper(ArrayWrapper&& other) : vals_( other.vals_ ), size_( other.size_ ), meta_( std::move( other.meta_ ) ) { other.vals_ = nullptr; } Pitäisiko kaikissa olla std::move?? 1 Olio o; 2 Olio&& returnrvalueref() //... 4 return std::move( o ); 5 } swap 1 template<typename T> 2 void swap(t& a, T& b) 4 T tmp(a); 5 a = b; 6 b = tmp; 7 } swap move 1 template<typename T> 2 void swap(t& a, T& b) 4 T tmp(std::move(a)); 12
5 a = std::move(b); 6 b = std::move(tmp); 7 } STL ja siirtosemantiikka Figure 1: http://cpp-next.com/archive/2010/10/howards-stl-move-semanticsbenchmark/ std::move() osa kaikkia siirtoja Tehokkaassa C++11-koodissa kannattaa käyttää std::move():a aina siirtoja toteutettaessa. 1 class Kanta {... }; 2 class Johdettu : public Kanta { 3 public: 4 Johdettu(Johdettu&& rhs) 5 : Kanta( std::move(rhs) ), 6 jasen_( std::move(rhs.jasen_) ) {}; 7 8 Johdettu& operator=(johdettu&& rhs) 9 { Kanta::operator=(std::move(rhs)); 10 jasen_ = std::move(rhs.jasen_); 11 return *this; 12 } 13
13... 14 }; perfect forwarding 1 template<typename T, typename Arg> 2 shared_ptr<t> factory(arg a) 4 return shared_ptr<t>(new T(a)); 5 } Ylimääräinen arvonvälitys (erityisen ongelmallinen jos rakentaja ottaa viiteparametrin) 1 template<typename T, typename Arg> 2 shared_ptr<t> factory(arg& a) 4 return shared_ptr<t>(new T(a)); 5 } factory<x>( kutsu() ); factory<x>(42); Lisätään rvalue:t huomioiva versio: 1 template<typename T, typename Arg> 2 shared_ptr<t> factory(arg const& a) 4 return shared_ptr<t>(new T(a)); 5 } 1. Useammalla parametrilla joudutaan tekemään kaikki kombinaatiot 2. Parametrit ovat aina lvalue-arvoja. Siirtosemantiikkaa ei pystytä hyödyntämään. 14
perfect forwarding 1 template<typename T, typename Arg> 2 shared_ptr<t> factory(arg&& a) 4 return shared_ptr<t>(new T( std::forward<arg>(a))); 5 } auto perfect forwarding 1 int i = 0; 2 int& j = i; 3 const int k = 0; 4 int& f(); 5 int g(); 6 7 auto&& a = i; // deduced: int& 8 auto&& b = j; // deduced: int& 9 auto&& c = k; // deduced: const int& 10 auto&& d = f(); // deduced: int& 11 auto&& e = g(); // deduced: int&& TAKISTUSPISTE Onko aikaa? std::move() toteutus Periaate helppo: 1. palauta aina rvalue 2. täytyy toimia lvalue ja rvalue -parametreilla, (cv huomioituna myös) 1 template <typename T> 2 T&& move( magic_ref_type param ) 4 return param; 5 } 15
Reference collapsing 1 template<typename T> 2 void f(t& param); 3 4 int x; 5 f<int&>(x) // T == int& Instantiointi antaa: 1 void f(int& & param); 2 // viite viitteeseen? C++98 sisältää säännön näiden romahduttamiseen : T& & T& collapsing rules C++11 T& & T& T&& & T& T& && T& T&& && T&& std::move() paluuarvo 1 template <typename T> 2 typename std::remove_reference<t>::type && 3 move( magic_ref_type param ) 4 { 5 return param; 6 } 1 int x; 2 std::move<int&>(x) 3 // remove_ref<int&>::type&& std::move(... 4 // -> int&& std::move( 16
Parametrin tyypin tulee toimia vakioiden ja rvalue:en kanssa. 1 template <typename T> 2 typename std::remove_reference<t>::type && 3 move( T&& param ) 4 { 5 return param; 6 } Viimeinen ongelma: nimetty parametri on lvalue, jota ei voi palauttaa rvalueviitteenä 1 template <typename T> 2 typename std::remove_reference<t>::type && 3 move( T&& param ) 4 { 5 using ReturnTYpe = 6 typename std::remove_reference<t>::type&&; 7 8 return static_cast<returntype>(param); 9 } std::forward() yksi toteutustapa 1 template <class T, class U, 2 class = typename enable_if< 3 (is_lvalue_reference<t>::value? 4 is_lvalue_reference<u>::value : 5 true) && 6 is_convertible<typename remove_reference<u>::type*, 7 typename remove_reference<t>::type*>::value 8 >::type> 9 inline 10 T&& 11 forward(u&& u) 12 { 13 return static_cast<t&&>(u); 14 } 17
[rglx]values Figure 2: decltype määrittely The type denoted by decltype(e) is defined as follows: if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e; otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e; otherwise, decltype(e) is the type of e. 18
Kitoooos 1 19