types_common_type
types_common_type (공통 타입 결정)
std::common_type은 여러 타입들이 공통으로 변환될 수 있는 타입을 결정하는 템플릿이에요. 이 페이지에서는 그 동작 방식과 규칙, 그리고 사용자 정의 특수화 방법을 설명할게요. C++11부터 <type_traits> 헤더에서 제공되고 있어요.
출처: cppreference
본문
<type_traits> 헤더에 정의되어 있어요.
| 정의 | |
|---|---|
template<class... T> struct common_type; |
(C++11부터) |
이 템플릿은 모든 T... 타입들 사이의 공통 타입을 결정해요. 즉, 모든 T...가 명시적으로 변환될 수 있는 타입이에요. 그런 타입이 아래 규칙에 따라 존재한다면, 멤버 타입 type이 그 타입을 가리켜요. 그렇지 않으면 멤버 타입 type은 존재하지 않아요.
sizeof...(T)가 0이면, 멤버 타입type이 없어요.sizeof...(T)가 1이면 (즉T...가 오직 하나의 타입T0만 포함), 멤버 타입은std::common_type<T0, T0>::type이 존재한다면 그와 같은 타입을 가리켜요. 존재하지 않으면 멤버 타입이 없어요.sizeof...(T)가 2이면 (즉T...가 정확히 두 타입T1과T2를 포함),T1또는T2중 적어도 하나에std::decay를 적용했을 때 다른 타입이 된다면, 멤버 타입은std::common_type<std::decay<T1>::type, std::decay<T2>::type>::type이 존재한다면 그와 같은 타입을 가리켜요. 존재하지 않으면 멤버 타입이 없어요.- 그렇지 않고,
std::common_type<T1, T2>에 대한 사용자 정의 특수화가 있다면 그 특수화가 사용돼요. - 그렇지 않고,
std::decay<decltype(false ? std::declval<T1>() : std::declval<T2>())>::type이 유효한 타입이라면, 멤버 타입은 그 타입을 가리켜요. 조건 연산자를 참고하세요. - (C++20부터) 그렇지 않고,
std::decay<decltype(false ? std::declval<CR1>() : std::declval<CR2>())>::type이 유효한 타입이라면 (여기서CR1과CR2는 각각const std::remove_reference_t<T1>&와const std::remove_reference_t<T2>&), 멤버 타입은 그 타입을 가리켜요. - 그렇지 않으면 멤버 타입이 없어요.
sizeof...(T)가 2보다 크면 (즉T...가 타입들T1, T2, R...로 구성),std::common_type<T1, T2>::type이 존재한다면, 멤버 타입은std::common_type<typename std::common_type<T1, T2>::type, R...>::type이 존재한다면 그 타입을 가리켜요. 그 외 모든 경우에는 멤버 타입이 없어요.
매개변수 팩 T 안의 어떤 타입이 불완전한 타입이거나, (cv 한정된) void이거나, 경계를 알 수 없는 배열이라면, 동작은 정의되지 않아요.
위 템플릿의 인스턴스화가 불완전한 타입에 직간접적으로 의존하고, 그 타입이 가상으로 완성되었을 때 다른 결과를 낳을 수 있다면, 동작은 정의되지 않아요.
멤버 타입
| 멤버 타입 | 설명 |
|---|---|
type |
모든 T에 대한 공통 타입 |
헬퍼 타입
| 템플릿 | 설명 |
|---|---|
template<class... T> using common_type_t = typename common_type<T...>::type; |
(C++14부터) |
특수화
사용자는 다음 조건을 만족하면 T1과 T2에 대해 common_type을 특수화할 수 있어요.
T1과T2중 적어도 하나가 사용자 정의 타입에 의존하고,std::decay가T1과T2모두에 대해 항등 변환이어야 해요.
그런 특수화에 type이라는 멤버가 있다면, 그것은 공용이고 모호하지 않은 멤버여야 하며, T1과 T2 둘 다 명시적으로 변환될 수 있는 cv 한정되지 않은 비참조 타입을 가리켜야 해요. 또한 std::common_type<T1, T2>::type과 std::common_type<T2, T1>::type은 같은 타입을 가리켜야 해요.
프로그램이 이러한 규칙을 위반하는 common_type 특수화를 추가하면 동작은 정의되지 않아요.
프로그램이 <type_traits>의 다른 템플릿에 특수화를 추가하면 (C++20부터 std::basic_common_reference 제외) 동작은 정의되지 않아요.
다음 특수화들은 표준 라이브러리에서 이미 제공되고 있어요.
| 특수화 | 설명 |
|---|---|
std::common_type<std::chrono::duration> (C++11) |
std::common_type 트레이트를 특수화해요 (클래스 템플릿 특수화) |
std::common_type<std::chrono::time_point> (C++11) |
std::common_type 트레이트를 특수화해요 (클래스 템플릿 특수화) |
std::common_type<std::pair> (C++23) |
두 pair의 공통 타입을 결정해요 (클래스 템플릿 특수화) |
std::common_type<tuple-like> (C++23) |
튜플과 튜플 유사 타입의 공통 타입을 결정해요 (클래스 템플릿 특수화) |
std::common_type<std::basic_const_iterator> (C++23) |
반복자와 적응된 basic_const_iterator 타입의 공통 타입을 결정해요 (클래스 템플릿 특수화) |
가능한 구현
// primary template (used for zero types)
template<class...>
struct common_type {};
// one type
template<class T>
struct common_type<T> : common_type<T, T> {};
namespace detail {
template<class...>
using void_t = void;
template<class T1, class T2>
using conditional_result_t = decltype(false ? std::declval<T1>() : std::declval<T2>());
template<class, class, class = void>
struct decay_conditional_result {};
template<class T1, class T2>
struct decay_conditional_result<T1, T2, void_t<conditional_result_t<T1, T2>>>
: std::decay<conditional_result_t<T1, T2>> {};
template<class T1, class T2, class = void>
struct common_type_2_impl : decay_conditional_result<const T1&, const T2&> {};
// C++11 implementation:
// template<class, class, class = void>
// struct common_type_2_impl {};
template<class T1, class T2>
struct common_type_2_impl<T1, T2, void_t<conditional_result_t<T1, T2>>>
: decay_conditional_result<T1, T2> {};
}
// two types
template<class T1, class T2>
struct common_type<T1, T2>
: std::conditional<
std::is_same<T1, typename std::decay<T1>::type>::value &&
std::is_same<T2, typename std::decay<T2>::type>::value,
detail::common_type_2_impl<T1, T2>,
common_type<typename std::decay<T1>::type, typename std::decay<T2>::type>
>::type {};
// 3+ types
namespace detail {
template<class AlwaysVoid, class T1, class T2, class... R>
struct common_type_multi_impl {};
template<class T1, class T2, class... R>
struct common_type_multi_impl<void_t<typename common_type<T1, T2>::type>, T1, T2, R...>
: common_type<typename common_type<T1, T2>::type, R...> {};
}
template<class T1, class T2, class... R>
struct common_type<T1, T2, R...>
: detail::common_type_multi_impl<void, T1, T2, R...> {};
참고 사항
승격(promotion)되지 않는 산술 타입의 경우, 공통 타입은 T0() + T1() + ... + Tn() 같은 (혼합 모드일 수 있는) 산술 표현식의 타입으로 볼 수 있어요.
예제
프로그램 정의 클래스에서 혼합 모드 산술을 보여줘요.
#include <boost/core/demangle.hpp>
#include <iostream>
#include <type_traits>
#include <typeinfo>
template<class T>
struct Number { T n; };
template<class T, class U>
constexpr Number<std::common_type_t<T, U>>
operator+(const Number<T>& lhs, const Number<U>& rhs)
{
return {lhs.n + rhs.n};
}
template<typename T>
std::ostream& operator<< (std::ostream& os, Number<T> x)
{
return os << boost::core::demangle(typeid(Number<T>).name()) << '{' << x.n << '}';
}
int main()
{
Number<int> i1{1}, i2{2};
Number<double> d1{2.3}, d2{3.5};
std::cout << "i1 + i2 == " << i1 + i2 << "\n"
"i1 + d2 == " << i1 + d2 << "\n"
"d1 + i2 == " << d1 + i2 << "\n"
"d1 + d2 == " << d1 + d2 << "\n";
}
출력:
i1 + i2 == Number<int>{3}
i1 + d2 == Number<double>{4.5}
d1 + i2 == Number<double>{4.3}
d1 + d2 == Number<double>{5.8}
결함 보고서
다음 동작 변경 결함 보고서는 이전에 발표된 C++ 표준에 소급 적용되었어요.
| DR | 적용 대상 | 발표된 동작 | 올바른 동작 |
|---|---|---|---|
| LWG 2141 | C++11 | 조건 연산자의 결과 타입이 decay되지 않았음 | 결과 타입을 decay함 |
| LWG 2408 | C++11 | common_type이 SFINAE 친화적이지 않았음 |
SFINAE 친화적으로 만듦 |
| LWG 2460 | C++11 | common_type 특수화를 작성하기가 거의 불가능했음 |
필요한 특수화 수를 줄임 |
같이 보기
common_with (C++20) |
두 타입이 공통 타입을 공유함을 지정해요 (개념) |
|---|---|
common_type (C++26) |
반영된 타입들의 시퀀스에 대한 공통 타입을 결정해요 (함수 템플릿) |