일단 Code Project의 "Ten C++11 Features Every C++ Developer Should Use"를 기반으로
C++11 feature들에 대해서 대충 정리 함.
[auto - deduction of a type from an initializer]
Deducing the type of variable from its initializer expression
말 그대로 생성자를 기반하여 변수의 type을 추정하게 하는 키워드. compile 시간에 사용 가능.
위 링크에서의 예제를 보면 바로 이해가 될 것임.
auto x = 7; // int
auto x = expression; // expression의 결과값의 type
auto v2 = 'c' // char
auto v3 {f()} // f()의 리턴값의 type
auto x0 {} // error
auto x3 {1,2,3} // 3개의 값을 가진 int list
|
템플릿 인자를 다룰 때 유용하게 사용되어짐.
C++11
template<class T> void printall(const vector<T>& v)
{
for (auto p = v.begin(); p!=v.end(); ++p)
cout << *p << "\n";
}
In C++98, we'd have to write
template<class T> void printall(const vector<T>& v)
{
for (typename vector<T>::const_iterator p = v.begin(); p!=v.end(); ++p)
cout << *p << "\n";
}
|
하지만 단순히 간단히 타이핑 하는 것으로 끝나는 것이 아니라
템플릿 인자를 사용해서 프로그래밍할 경우 좀 애매한 경우에 유용하다.
템플릿 인자를 그대로 사용할 때는 괜찮지만 템플릿 인자들을 연산해서 나오는 결과가 무엇인지 몰라 구체화 하지 못할 경우일때가 하나의 예이다.
template<class T, class U> void multiply(const vector<T>& vt, const vector<U>& vu)
{
// ...
auto tmp = vt[i]*vu[i];
// ...
}
|
그리고 해당 링크에서 보면 auto feature는 1984에 개발되었지만 C compatibility 문제로 인해서 제외가 되었었다고 함. 하지만 이전 auto의 개념("this is a local variable")은 지금과 다르다고 하다. (하지만 난 history도 C++98도 몰라서.. 자세히는 모르겠다.)
추가로 trailing(suffix) return type 예를 codeproject 링크에서 보여주고 있음.
template <typename T1, typename T2>
auto compose(T1 t1, T2 t2) -> decltype(t1 + t2) { return t1+t2; } auto v = compose(2, 3.14); // v's type is double |
trailing(suffix) return type 관련해서 사용되게 된 이유를 ibm 블로그 글에서 설명이 잘 되어 있음.
decltype()을 사용하여 a*b type으로 선언 하여 사용하고자
왼쪽의 예를 오른쪽 처럼 바꿔서 사용할 경우
컴파일러의 parsing 순서 상(왼쪽에서 오른쪽으로) a,b의 type을 확인하지 못해서 (a*b를 확인할 때 a,b가 선언되어 있지 않아서) 에러가 발생된다.
template<class T,class U>
U mul(T a, T b){
return a*b;
}
|
template<class T>
decltype(a*b) mul(T a, T b){
return a*b;
}
|
그래서 이런 문제를 해결하고자 사용 하는 방법이 trailing(suffix) return type임.
template<class T>
auto mul(T a, T b) -> decltype(a*b){
return a*b;
}
|
위의 feature들을 사용하면 아래와 같이 아무런 type을 지정하지 않고도 코딩이 된 예제가 나올 수 있음.
#include <iostream>
using namespace std;
template<typename T1, typename T2>
auto sum(T1 & t1, T2 & t2) -> decltype(t1 + t2){
return t1 + t2;
}
int main(){
auto i = 1;
auto j = 1.3;
auto k = sum(a,b);
cout << c << endl;
}
|
[참고]
- constexpr
변수의 값이나 함수의 결과값을 constant 속성을 부여하여 compile 시에 사용될 수 있도록 함.
아래 예제에서 보면 factorial, conststr, countlower함수 들이 constexpr로 선언되어 compile 시점에 out1, out2의 template인자로 사용되었음.
#include <iostream>
#include <stdexcept> // constexpr functions use recursion rather than iteration constexpr int factorial(int n) { return n <= 1 ? 1 : (n * factorial(n-1)); } // literal class class conststr { const char * p; std::size_t sz; public: template<std::size_t N> constexpr conststr(const char(&a)[N]) : p(a), sz(N-1) {} // constexpr functions signal errors by throwing exceptions from operator ?: constexpr char operator[](std::size_t n) const { return n < sz ? p[n] : throw std::out_of_range(""); } constexpr std::size_t size() const { return sz; } }; constexpr std::size_t countlower(conststr s, std::size_t n = 0, std::size_t c = 0) { return n == s.size() ? c : s[n] >= 'a' && s[n] <= 'z' ? countlower(s, n+1, c+1) : countlower(s, n+1, c); } // output function that requires a compile-time constant, for testing template<int n> struct constN { constN() { std::cout << n << '\n'; } }; int main() { std::cout << "4! = " ; constN<factorial(4)> out1; // computed at compile time volatile int k = 8; // disallow optimization using volatile std::cout << k << "! = " << factorial(k) << '\n'; // computed at run time std::cout << "Number of lowercase letters in \"Hello, world!\" is "; constN<countlower("Hello, world!")> out2; // implicitly converted to conststr } |
- decltype()
초기값이 없어 type을 추론하지 못할 경우 변수나 표현식의 declared type을 사용하여 선언할 수 있게 해주는 keyword
#include <iostream>
struct A { double x; }; const A* a = new A(); decltype( a->x ) x3; // type of x3 is double (declared type) decltype((a->x)) x4 = x3; // type of x4 is const double& (lvalue expression) template <class T, class U> auto add(T t, U u) -> decltype(t + u); // return type depends on template parameters int main() { int i = 33; decltype(i) j = i*2; std::cout << "i = " << i << ", " << "j = " << j << '\n'; auto f = [](int a, int b) -> int { return a*b; }; decltype(f) f2{f}; // the type of a lambda function is unique and unnamed i = f(2, 2); j = f2(3, 3); std::cout << "i = " << i << ", " << "j = " << j << '\n'; } |
- trailing(suffix) return type
간단히 말하면 함수의 뒷부분에 함수의 리턴 값의 type을 명시하는 것이다.(2)
일반적으로는 함수의 처음에 리턴값의 type을 명시하는 prefix return type을 사용함(1)
(2)예: (1) string to_string(int a); (2) auto to_string(int a) -> string;
설명은 위를 참고하고 Ibm blog 글에서 아래와 같은 예제를 보여줌..
가독성 측면에서도 유용하게 사용될 수 있을 것으로 보임.
int형 템플릿 인자를 가지는 tmp class를 리턴하는 함수 포인터인 foo를
trailing return type으로 표시할 경우 아래와 같다는..
template <class T> class tmp{
public:
int i;
};
tmp<int> (*(*foo())())() {
return 0;
}
|
template <class T> class tmp{
public:
int i;
};
auto foo(<wbr />)->a<wbr />uto(<wbr />*)()<wbr />->tm<wbr />p<in<wbr />t>(*<wbr />)()<wbr />{
return 0;
}
|
댓글 없음:
댓글 쓰기