I'm FanJae.

[C++] Explicit Constructor 본문

C++/Basic

[C++] Explicit Constructor

FanJae 2024. 8. 14. 12:38

1. 복사 초기화 과정의 문제점

class Vector
{
public:
	Vector(int size){}
};

int main()
{
   // C++98
   Vector v1(10);
   Vector v2 = 10;
   
   // C++11
   Vector v3{10};
   Vector v4 = {10};
   
   v1 = 20;
}

- 위 예제는 인자가 한 개(int)인 생성자가 있는 타입의 객체로 총 4가지의 객체 생성 방법을 보인다.

- 이 때, '='가 있으면 Copy Initialization(복사 초기화)가 되고 없으면, Direct Initialization(직접 초기화)가 된다.

 

- 인자가 한 개인 생성자는 변환의 용도로도 사용이 가능하다.

- 이를 변환 생성자(Conversion constructor)라고도 한다.

v1 = 20; // v1 = Vector(20)

- 즉, 이 경우는 벡터 임시 객체를 만들어서 v1에 보내는 것이다.

class Vector
{
public:
	Vector(int size){}
};
void foo(Vector v)
{
}
int main()
{
	Vector v1(10);   
	Vector v2 = 10; 
	Vector v3{10};   
	Vector v4 = {10}

	v1 = 20; 	

	foo(v1);
	foo(10); // ?
}

- 위 예제에서, foo(10) 부분을 풀어 보면, 아래와 같이 풀릴 것이다.

Vector v = 10;

- 이와 같이 변환 한다고 생각하면 논리적으로, foo(10)은 아무런 문제가 되지 않는것처럼 보인다

- 다만,이와 같은 행위가 실수일 수 있었음에도 에러가 발생하지 않은 것일 수 있다.


2. Explicit 생성자

- Direct Initialization만 가능하고, Copy Initialization은 사용할 수 없다.

- 암시적 변환의 용도로 사용될 수 없다. 명시적 변환은 사용가능하다.

class Vector
{
public:
	explicit Vector(int size){}
};
void foo(Vector v)
{
}
int main()
{
	Vector v1(10);   // ok
	Vector v2 = 10;  // error
	Vector v3{10};   // ok
	Vector v4 = {10} // error

	v1 = 20; 	     // error

	foo(v1);         // ok
	foo(10);         // error
}

- 복사 초기화는 함수 인자를 전달하는 원리인데 다소 위험한 상황이 발생할 수 있다.

- 따라서, 안전하게 하기 위해 explicit로 선언하여 잘못된 인자가 가는것을 막는것이다.

 

2-1. STL과 Explicit 생성자

- STL의 경우는 생성자가 Explicit인 것도 있고, 아닌것도 있다.

#include <vector>
#include <string>

void f1(std::string s)     {}
void f2(std::vector<int> v){}

int main()
{
	std::string s1("hello");
	std::string s2 = "hello"; // ok

	std::vector<int> v1(10);
	std::vector<int> v2 = 10; // error

	f1("hello"); // ok. string s = "hello"
	f2(10);      // error. vector<int> v = 10

	std::vector<int> v3 = 10;   // error
	std::vector<int> v4 = {10}; // ok
}

- f1("hello")는 string s = "hello"를 보내는 행위로 논리적으로 가능하다. 

- f2(10); 은 vector<int> v = 10이다.이는 논리적으로 다소 이상해보일 수 있다. 

- 실제로 f1은 Compile Error가 아니나 f2는 Compile Error가 발생한다.

 

※ 즉, std::string은 Explicit가 아니고, std::vector는 생성자를 Explicit로 작성했음을 알 수 있다.

 

- 문제는 여기서, vector 사용시 ()와 { }의 차이가 있다.

std::vector<int> v3 = 10;   // error
std::vector<int> v4 = {10}; // ok

- vector<int> v(10);의 경우 크기가 10개인 vector를 부르는 것이다.

-이때는 explicit vector(std::size_t)를 호출하여, Explicit이기 때문에 에러가 발생한다

 

- 한편, vector<int> v {10}; 과 같은 경우, 크기가 한 개(초기값 10)인 vector이다.

- 이때는 vector(std::initializer_list)를 호출하는 것으로 Explicit 생성자가 아니다.

 

이후, Initializer list에 대해 얘기할 떄, 다시 언급하고자 한다. 일단, 이것이 다른 생성자를 부른다는 사실을 알아두자.


3. Explicit의 또다른 특징

class Object
{
public:
      Object() {}
      Object(int a, int b) {}
};
void foo ( Object obj ) {}

int main()
{
    Object o1;
    Object o2{};
    Object o3 = {};
    
    Object o4(1,2);
    Object o5{1,2};
    Object o6 = {1,2};
    
    foo({}); // Object obj {}
    foo({1,2}); //Object obj = {1,2};
    
 }

- 위 예제에서 foo({}), foo({1,2})와 같이 보내는 것도 문제되지 않는다.

class Object
{
public:
	Object() {}
	explicit Object(int a, int b) {}
};
void foo( Object obj ) {}

int main()
{
	Object o1;
	Object o2{};
	Object o3 = {};

	Object o4(1,2);
	Object o5{1,2};
	Object o6 = {1,2}; // Error

	foo({});    // Object obj = {}
	foo({1,2}); // Object obj = {1,2}; Error
}

- 이와 같이 2개의 인자 값을 받는 경우에 대해서 Explicit를 붙이면 o6과 foo({1,2}) 같이 복사 초기화가 일어나는 경우를 허용하지 않는다.

※ 즉, 인자의 개수가 없거나 여러 개인 경우에도 Explicit를 붙이는 것이 가능하다.


4) explicit(bool) // C++20

#include <type_traits>

class Object
{
public:
     explicit(false) Object(int a) {}
     
 };
 
 int main()
 {
     Object o1(10);
     Object o2 = 10;
 }

- 특정 객체에 대한 Explicit 여부를 결정할 수 있다. 

- 이는 주로 클래스 템플릿을 만들때 활용된다.

#include <type_traits>

class Object
{
public:
     template<typename T>
     explicit( std::is_integral_v<T> ) Object ( T args )
};

int main()
{
    Object o1(10);
    // Object o2 = 10;
    Object o2 = 3.4;
}

- 위와 같이 std::is_integral_v<T>를 이용해서 정수형인지 여부를 컴파일 타입에 판별하고, 이에 따라서 Explicit 여부를 결정하는데 사용할 수 있다.

 

'C++ > Basic' 카테고리의 다른 글

[c++] const member function, mutable  (0) 2024.08.17
[C++] Static Member  (0) 2024.08.16
[C++] Member initializer list, Default member initializer  (0) 2024.08.14
[C++] Vector I  (0) 2024.08.13
[C++] Constructor / Destructor  (0) 2024.08.13
Comments