일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- diamond inheritance
- placement new
- c++ multi chatting room
- increment operator
- delete function
- virtual destructor
- constructor
- C++
- 더 지니어스 양면포커
- this call
- new&delete
- return by reference
- virtual function
- discord bot
- std::endl
- c++ basic practice
- std::vector
- base from member
- suffix return type
- conversion constructor
- vector capacity
- vector size
- operator overloading
- virtual inheritance
- virtual function table
- pointer to member data
- member function pointer
- std::ostream
- dynamic_cast
- std::cout
- Today
- Total
I'm FanJae.
[C++ Intermediate] Trivial 본문
1. Trivial
1-1. Special Member Function
- 사용자가 제공하지 않으면 컴파일러가 제공하는 멤버 함수가 존재한다.
① 디폴트 생성자(Default Constructor)
② 소멸자(Destructor)
③ 복사 생성자(Copy Constructor)
④ 복사 대입연산자(Copy Assignment)
⑤ 이동 생성자(Move Constructor)
⑥ 이동 대입연산자(Move Assignment)
※ 보통 이러한 멤버 함수들을 Trivial 하다고 한다.
2. Trivial Default Constructor
※ 예제가 아주 많아서 check 함수는 한번만 보이고, 이후 부터는 임의 생략한다.
① The constructor is not user-provided
② T has no virtual member functions
③ T has no virtual base classes
④ T has no non-static members with default initializers.
⑤ Every direct base of T has a trivial default constructor
⑥ Every non-static member of class has a trivial default constructor
- 디폴트 생성자가 Trivial 하다는 의미는 컴파일러가 생성하는 디폴트 생성자가 아무일도 하지 않는 경우이다.
- 조사하는 방법은 type_traits를 사용하면 된다.
#include <iostream>
#include <type_traits>
struct TrivialDefaultCtor // true
{
int data;
};
struct NonTrivialDefaultCtor
{
int data;
NonTrivialDefaultCtor() {}
};
template<class T> void check()
{
std::cout << typeid(T).name() << " : ";
std::cout << std::boolalpha;
std::cout << std::is_trivially_default_constructible_v<T> << std::endl;
}
int main()
{
check<TrivialDefaultCtor>(); // True
check<NonTrivialDefaultCtor>(); // False
}
- TrivialDefaultCtor은 컴파일러가 Default 생성자를 만들고 아무것도 안한다. 따라서 True가 나온다.
- 반면, NonTrivialDefaultCtor은 사용자가 만들었기 때문에 False가 된다.
struct Type1
{
// 컴파일러가 디폴트 생성자 제공안함. - false
Type1(int a) {}
};
struct Type2
{
Type2() {}; // false
Type2(int a) {}
};
struct Type3
{
Type3() = default; // true
Type3(int a) {}
};
struct Type4 // false
{
int data = 0;
//
// int data;
// Type4() : data(0) {}
};
- Type1의 경우는 컴파일러가 별도의 디폴트 생성자를 제공 안한다. 따라서 False이다.
- Type2의 경우는 사용자가 만들었기 때문에 False이다.
- Type3의 경우는 default 생성자를 사용자에게 만들어달라고 요구했고, 아무것도 안한다. 따라서 True이다.
struct Type4 // false
{
int data = 0;
// 위와 같이 만들면 아래와 같이 코드를 변경한다.
// int data;
// Type4() : data(0) {}
};
struct Type5 // false
{
int data;
virtual void foo() {}
};
struct Type6 // false
{
int data1;
NonTrivialDefaultCtor data2;
// Type6() : data2() {} // 컴파일러가 추가한 코드
};
- Type4의 경우는 초기화 과정에서 변환이 일어난다. 초기화 때문에 trivial 하지 않다.
- Type5의 경우는 가상 함수가 있는 경우 가상 함수 테이블을 초기화 하는 작업이 있다. 따라서, trivial 하지 않다.
- Type6의 경우는 trival 하지 않은 멤버 데이터가 있다. 따라서 trivial 하지 않다.
struct Type7 // true
{
int data1;
TrivialDefaultCtor data2;
// Type7() : data2() {} // 컴파일러가 추가한 코드
};
// false
struct Type8 : public NonTrivialDefaultCtor
{
int data;
};
// true
struct Type9 : public TrivialDefaultCtor
{
int data;
};
- Type7의 경우는 멤버 데이터가 Trivial 하다. 컴파일러가 추가한 생성자에서는 아무것도 하지 않는다. 따라서 trivial 하다.
- Type8의 경우는 기반 클래스의 생성자를 호출 하지만, 이것이 trivial하지 않아서 trivial하지 않다.
- Type9의 경우는 기반 클래스의 생성자를 호출 하지만, 이는 trivial이다. 따라서 trivial하다.
// false
struct Type10 : public virtual TrivialDefaultCtor
{
};
// false
struct Type11
{
Type11() = delete;
};
// false
struct Type12
{
int& ref; // 참조 멤버가 있으면
// 디폴트 생성자는 =delete됨.
};
// false
struct Type13
{
const int c;
};
- Type10의 경우 trivial 한 Default 생성자를 가진 것을 상속 받지만 Virtual 상속이다.
- 이 경우, 가상 상속을 위한 초기화 구문이 필요하기 때문에 Type10은 trivial 하지 않다.
- Type11의 경우, default 생성자를 임의로 삭제했다. 컴파일러가 생성하는 생성자없기에 trivial 하지 않다.
- Type12의 경우, 초기값이 없는 참조는 만들 수 없다. 참조가 멤버로 있으면 디폴트 생성자는 delete 된다.
- 따라서, Type12도 trivial 하지 않다.
- Type13의 경우, 상수 멤버가 있다. 초기화 되지 않은 상수가 만들어지기 때문에, Default 생성자가 삭제된다.
- Type12와 비슷한 이유로 trivial하지 않다.
1-3. Assembly Level
- 생성자 호출을 확인할 수 있다.
- 하지만 이와 같이 생성자를 삭제할 경우, main 에서 생성자를 호출 하는 부분이 사라지는 것을 확인할 수 있다.
- default 와 같이 적었을 때도 결과는 동일하게 나오는 것을 확인할 수 있다.
- 가상 함수를 만들었을때는 다음과 같이 생성자의 호출을 확인 할 수 있다.
※ C++ 표준에서 언급되는 얘기는 아니다. 최적화 옵션이 들어가면 달라질 수 있는 사항이다.
3. Trivial Copy Constructor
#include <iostream>
#include <string>
#include <type_traits>
class Point
{
int x;
int y;
public:
Point() = default;
Point(int a, int b) : x(a), y(b) {}
};
int main()
{
std::cout << std::is_trivially_copy_constructible_v<Point> << std::endl;
Point pt1(1,2);
Point pt2 = pt1;
Point pt3;
memmove(&pt3, &pt1, sizeof(Point));
}
- 컴파일러가 제공하는 복사 생성자가 메모리 복사 이외 어떤 일도 수행하지 않으면 Trivial 하다고 한다.
- 사용자가 직접 객체를 memmove 등으로 복사하는 것과 동일하다.
"effectively copies every scalar subobject (including, recursively, subobject of subobjects and so forth) of the argument and performs no other action"
① It is not user-provided (that is, it is implicitly-defined or defaulted)
② T has no virtual member functions
③ T has no virtual base classes
④ the copy constructor selected for every direct base of T is trivial;
⑤ the copy constructor selected for every non-static class type (or array of class type) member of T is trivial
3-1. Trivial 복사 생성자의 활용 예시
#include <iostream>
#include <type_traits>
struct Point
{
int x = 0;
int y = 0;
};
template<class T>
void copy_type(T* dst, T* src, std::size_t sz)
{
}
int main()
{
Point arr1[5];
Point arr2[5];
copy_type(arr1, arr2, 5);
}
- T의 복사 생성자가 trivial 하다면, 배열 전체를 memcpy나 memmove 등으로 복사하는 것이 빠르다.
- T의 복사 생성자가 trivial 하지 않다면, 배열의 모든 요소에 대해 하나씩 복사 생성자를 호출해서 복사해야 한다.
#include <iostream>
#include <type_traits>
struct Point
{
int x = 0;
int y = 0;
};
template<class T>
void copy_type(T* dst, T* src, std::size_t sz)
{
if constexpr ( std::is_trivially_copy_constructible_v<T> )
{
std::cout << "using memcpy" << std::endl;
memcpy(dst, src, sizeof(T)*sz);
}
else
{
std::cout << "using copy ctor" << std::endl;
while(sz--)
{
new(dst) T(*src);
--dst, --src;
}
}
}
int main()
{
Point arr1[5];
Point arr2[5];
copy_type(arr1, arr2, 5);
}
- 위와 같은 복사 기법을 많이 사용한다.
'C++ > Intermediate' 카테고리의 다른 글
[C++ Intermediate] Member Function Pointer (2) | 2024.09.23 |
---|---|
[C++ Intermediate] this call (1) | 2024.09.22 |
[C++ Intermediate] new/delete, placement new (0) | 2024.09.18 |
[C++ Intermediate] 객체의 변환(Conversion) (0) | 2024.09.13 |
[C++ Intermediate] Constructor 생성 원리 (1) | 2024.09.10 |