"(c++20) Conditional Explicit Constructor"
1. explicit // c++11
struct NoExplicit
{
NoExplicit() = default;
NoExplicit(int n)
{
cout << n << endl;
}
};
void TestNoExplicit(NoExplicit a)
{
}
int main(void)
{
NoExplicit a;
TestNoExplicit(a);
TestNoExplicit(10); // - ???? error X
}
TestNoExplicit(10); // - ???? error X
TestNoExplicit() NoExplicit struct 자료형을 매개변수로 받고 있다. 그럼 매개변수에 NoExplicit struct 이 외에 다른 자료형이 들어간다면 에러를 뱉어내는게 정상이다. 하지만 위 코드에서 TestNoExplicit(10) 에러를 뱉어 낼거 같지만 정상 작동한다.
이는 컴파일러에서 "10"을 NoExplicit(int n)" 생성자를 이용하여 암시적 형변환을 허용하기 때문에 이다.
이러한 암시적 형변환을 제한 하는 키워드가 바로 c++11에 추가 된 [explicit: 명확하다. 명시적이다.] 이다. 즉, 내가 지정한 생성자 자료형(클래스, 구조체) 외에는 명시적인 형변환을 허용하지 않겠다는 뜻이다.
이 키워드를 NoExplicit 함수에 붙여주게 되면,
explicit NoExplicit(int n)
{
cout << n << endl;
}
TestNoExplicit(10); 이 부분이 암시적 형변환으로 error를 뱉지 않았는데 NoExplicit 생성자에 explicit를 붙여줌으로서 TestNoExplicit(10);는 error 뱉는다.
2. conditional explicit // c++20
c++20부터는 explicit(bool)이 추가되어 특정 조건일 떄만 explicit로 동작하게 할 수 있다.
condition explicit : 조건 부로 원하지 않은 형변환이 일어나지 않도록 제한한다.
struct ConditionExplicit
{
ConditionExplicit() = default;
template<typename T>
explicit (!std::is_same < T, bool>::value) ConditionExplicit( T n)
{
cout << n << endl;
}
};
[1번]
void f(std::string);
f("hello"); //문자 리터럴 명시적 형변환
f("hello"sv); // std::string_view 명시적 형변환 허용x
[2번]
template<class T>
struct wrapper {
template <class U>
wrapper(U const& u) : t_(u) {}
T t_;
};
void g(wrapper<std::string>);
g("hello"); //this should compile
g("hello"sv); //this should not
[1번]은 f("hello"sv) 는 컴파일 error를 발생시키고, [2번]의 g("hello"sv)는 컴파일 error가 발생하지 않는다.
이를 해결하기위해 특정 컴파일 조건에서 함수 템플릿을 무시할수 있도록 해주는 표준 라이브러리의 헬퍼 템플릿 std::enable_if<>를 사용하여 작성하면 해결 할 수 있다.
template<class T>
struct wrapper {
template<class U, std::enable_if_t<std::is_convertible_v<U, T>>* = nullptr>
wrapper(U const& u) : t_(u) {}
template<class U, std::enable_if_t<!std::is_convertible_v<U, T>>* = nullptr>
explicit wrapper(U const& u) : t_(u) {}
T t_;
};
위 코드는 실제로 하나이여야 하는 코드가 두개 존재하고 SFINAE(substiution failure is not an error/치환 실패는 에러가 아니다)를 사용하여 둘 중 하나를 선택한다. 이번 c++20에 추가된 conditional explicit는 이를 한 번에 해결한다.
template<class T>
struct wrapper {
template<class U>
explicit(!std::is_convertible_v<U, T>)
wrapper(U const& u) : t_(u) {}
T t_;
};
void g(wrapper<std::string>);
g("hello");
g("hello"sv);
※ is_convertible_v 템플릿은 변환할 원본 형식 U, 변환할 대상 형식T (U=T)이 올바른 형식인 경우 true그렇지 않은 경우 false를 return 한다.
위 코드를 설명하면 아래와 같다.
조건부로 원하지 않는 형변환을 일어나지 않도록 막을 수 있도록 explicit(bool) bool 부분에 조건을 넣어준것이다.
explicit(!std::is_convertible_v<U, T>):
!(T가 U로 변화가능할 때 == std::is_convertible_v<U,T> == true) = T가 U로 변환가능하지 않을 때만 explicit 이다.
g("hello"sv)
U: std::string
T: std::string_view
T( std::string_view )가 U( std::string )로 변환이 가능하지 않아서 암시적 형변환을 허용하지 않는다. explicit로 차단함.
void g(wrapper<std::string_view>);
g함수 템플릿 매개변수가 string_view로 지정한다면
g("hello"sv)
U: std::string_view
T: std::string
T( std::string )가 U( std::string_view )로 변환이 가능하여 암시적 형변환을 허용한다.
※ enable_if
enable_if는 c++의 난해한 특성 중 하나인 SFINAE(substiution failure is not an error/치환 실패는 에러가 아니다)에 기반을 두고 있다. SFINAE(substiution failure is not an error/치환 실패는 에러가 아니다) : 오버로딩된 함수가 여러 개 있을 때 enable_if를 이용하여 특정한 타입 트레이트에 따라 오버로딩된 함수 중 일부를 끌 수 있다. enable_if트레이트는 오버로딩 함수들에 대한 리턴 타입을 기준으로 분기할 때 주로 사용된다. enable_if는 템플릿 타입 매개변수를 두 개 받는다. 하나는 bool. 다른 하나는 타입인데 디폴트값은 void다. C++ 표준은 enalbe_if처럼 type멤버를 가진 트레이트에 대한 앨리어스템플릿을 몇가지 정의하고 있고, 각각의 이름은 트레이트 이름 뒤에 _t가 붙어 있다. typename enable_if<...., bool>::type enable_if_t<...., bool> enable_if_t의 첫번 째 인수가 true면 enable_if_t는 bool 타입을 갖고, 그렇지 않으면 타입이 없다. 바로 여기서 SFINAE가 적용된다. 인스턴스화 하는 과정에서 아무런 문제가 발생하지 않으면 컴파일러는 이 버전의 check_type()을 적용한다.
1. 내용 출저: 인프런 Inflearn - Rookiss 쌤 강의인 C++20 훑어보기 중.
2. https://devblogs.microsoft.com/
'👨🏻💻 programming > ◽ c, c++' 카테고리의 다른 글
(c++20) STL::Container #2 - contains, starts_with, ends_with (2) | 2024.06.17 |
---|---|
(c++20) STL::Container #1 - std::to_array, erase, erase_if (0) | 2024.06.17 |
(c++20) [Three-way Comparsion(3방향 비교 연산자)/우주선] 연산자 (0) | 2024.06.14 |
(c++20) consteval, constinit (0) | 2024.06.14 |
[c++17] 전문자를 위한 c++17 새로운 내용 북마크 (0) | 2024.03.11 |
안 하는 것 보다 낫겠지
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!