«   2024/04   »
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
Archives
Today
Total
04-20 00:39
관리 메뉴

lancelot.com

Empty class 와 Tag dispatching, [[no_unique_address]] 을 이용한 EBCO 본문

프로그래밍

Empty class 와 Tag dispatching, [[no_unique_address]] 을 이용한 EBCO

lancelot50 2022. 7. 24. 10:45
  • empty class member는 크기에 포함되지 않음
  • [[no_unique_address]] : Empty class 일 때, 독립적인 주소를 가질 필요가 없다
  • C++20 에서 추가
// Visual Studio 2022 는 2022/07/24 현재 지원하지 않음

#include<iostream>
struct Empty1 {};
struct Empty2 {};

struct Data1	// sizeof : 4
{
	[[no_unique_address]] Empty1 e1;
	[[no_unique_address]] Empty2 e2;
	int data;
};

struct Data2	// sizeof : 1
{
	[[no_unique_address]] Empty1 e1;
};

struct Data3	// sizeof : 1
{
	[[no_unique_address]] Empty1 e1;
	[[no_unique_address]] Empty2 e2;
};

struct Data4	// sizeof : 2
{
	[[no_unique_address]] Empty1 e1;
	[[no_unique_address]] Empty1 e2;
};

int main()
{
	Data1 d1;
	Data2 d2;
	Data3 d3;
	Data4 d4;
	std::cout << "size : " << sizeof(d1) << std::endl;
	std::cout << "size : " << sizeof(d2) << std::endl;
	std::cout << "size : " << sizeof(d3) << std::endl;
	std::cout << "size : " << sizeof(d4) << std::endl;

	std::cout << "d3.e1 address : " << &(d3.e1) << std::endl;
	std::cout << "d3.e2 address : " << &(d3.e2) << std::endl;
	std::cout << "d4.e1 address : " << &(d4.e1) << std::endl;
	std::cout << "d4.e2 address : " << &(d4.e2) << std::endl;
}

 

  • [[no_unique_address]] 를 이용하면, template partial specialization 을 이용해서 구현했던 empty class 부분의 구현을 줄일 수 있다.
#include<iostream>
#include<string>
#include<type_traits>

class Empty { };

struct one_and_variadic_arg_t { explicit one_and_variadic_arg_t() = default; };
struct zero_and_variadic_arg_t { explicit zero_and_variadic_arg_t() = default; };

template<typename T1, typename T2> struct compressed_pair
{
	// [[no_unique_address]] Empty class 타입의 멤버가 독립적인 주소를 가질 필요가 없다
	//                       Empty class 멤버가 크기에 포함되지 않음 - C++20
	[[no_unique_address]] T1 first;	
	[[no_unique_address]] T2 second;

	constexpr T1& getFirst() noexcept { return first; }
	constexpr T2& getSecond() noexcept { return second; }

	constexpr const T1& getFirst() const noexcept { return first; }
	constexpr const T2& getSecond() const noexcept { return second; }

	template<typename A1, typename ... A2>
	constexpr compressed_pair(one_and_variadic_arg_t, A1&& arg1, A2&& ... arg2)
		noexcept (
		std::conjunction_v< std::is_nothrow_constructible<T1, A1>, std::is_nothrow_constructible<T2, A2...> > )
		: first(std::forward<A1>(arg1)), second(std::forward<A2>(arg2)... ) {}


	template<typename ... A2>
	constexpr compressed_pair(zero_and_variadic_arg_t, A2&& ... arg2) noexcept(
		std::conjunction_v< std::is_nothrow_default_constructible<T1>, std::is_nothrow_constructible<T2, A2...> > ) 
		: first(), second( std::forward<A2>(arg2)... ) {}
};

int main()
{
	compressed_pair<int, int> p1(one_and_variadic_arg_t{}, 1,1);
	compressed_pair<Empty, int> p2(zero_and_variadic_arg_t{}, 1);
	compressed_pair<int, Empty> p3(zero_and_variadic_arg_t{} );
	compressed_pair<Empty, Empty> p4(zero_and_variadic_arg_t{} );
	
	std::cout << sizeof(p1) << std::endl;	// 8
	std::cout << sizeof(p2) << std::endl;	// 4
	std::cout << sizeof(p3) << std::endl;	// 4
	std::cout << sizeof(p4) << std::endl;	// 2
	return 0;
}