[英]Why a template specialization cannot change the return type?
在閱讀完這個問題后,我不得不再次意識到我對模板知之甚少。 我可以理解,這樣的模板專業化
// A
template <typename T> void foo(T x){}
template <> void foo<double>(int x){}
無法正常工作( error: template-id 'foo<double>' for 'void foo(int)' does not match any template declaration
)。 它不僅沒有意義,而且參數演繹也沒有機會獲得正確的T
但是,我不明白為什么它對返回類型不起作用:
// B
template <typename T> int foo(T x){}
template <> double foo<double>(double x){}
(與上面類似的錯誤)。 實際上我手頭沒有任何特定的用例,但我仍然會對如何根據T
選擇返回類型感興趣。 作為一種解決方法,我發現了這個:
// C
template <typename T> struct foo { static int moo(T x){return x;} };
template <> struct foo<double> { static double moo(double x){return x;} };
因此可以選擇依賴於T
的返回類型。 不過,我仍然感到困惑......
B
不可能的原因是什么?
即使很奇怪,你也可能有
template <typename T>
void foo(int);
template <typename T>
char foo(int);
所以你的專業化將是模棱兩可的。
實際上,您可以通過使用返回類型的模板參數或traits類來解決它。
舉個例子:
#include<type_traits>
template<typename T>
T f() { return T{}; }
template<typename>
struct Traits;
template<>
struct Traits<int> {
using ReturnType = char;
};
template<>
struct Traits<char> {
using ReturnType = int;
};
template<typename T>
typename Traits<T>::ReturnType g() { return T{}; }
int main() {
static_assert(std::is_same<decltype(f<int>()), int>::value, "!");
static_assert(std::is_same<decltype(f<double>()), double>::value, "!");
static_assert(std::is_same<decltype(g<int>()), char>::value, "!");
static_assert(std::is_same<decltype(g<char>()), int>::value, "!");
}
在g
的情況下,如果推斷出參數,那么兩個看似相同的調用具有不同的返回類型。
也就是說,專業化不允許用戶更改函數的聲明。
這就是為什么你必須使用這樣的定義來為不同的模板參數提供不同的返回類型。
您的案例“C”可以使用默認模板參數輕松解決:
template<typename T, typename U = int>
U foo(T x) {
return x;
}
template<>
double foo<double, double>(double x) {
return x;
}
現在foo
可以像使用單個模板參數的函數一樣使用:
auto a = foo(5);
auto b = foo(1.0);
auto c = foo<short>(5);
另一種方法更加丑陋,但更為普遍。 它將要求您在一個位置枚舉所有可能的返回類型,允許您根據模板參數類型選擇任何返回類型。 這個解決方案的關鍵只是一個編譯時類型的映射。 它可以使用pair,tuples和tuple_element
以各種方式實現,但讓我們停止在一個簡約的實現上:
struct last_key{};
struct last_value{};
template<typename key, typename k = last_key, typename v = last_value, typename ...pairs>
struct type_map {
static_assert(sizeof...(pairs) % 2 == 0, "Last key does not have a value");
using type = typename std::conditional<std::is_same<key, k>::value,
v,
typename type_map<key, pairs...>::type>::type;
};
template<typename key>
struct type_map<key> {
using type = int;
};
對於給定的鍵類型,映射“返回”值類型,如果未找到鍵,則返回int
。 有了map,讓我們聲明一個依賴於單個模板參數的返回類型:
template<typename key>
using return_type = typename type_map<key, double, double>::type;
最后你的示例案例“C”將再次通過foo
的唯一聲明來解決:
template<typename T>
auto foo(T x) -> return_type<T> {
return x;
}
但是如果你願意,你仍然可以添加具有不同行為的特化,這將編譯並正常工作:
// Specialization.
template<>
auto foo(double x) -> double {
return x;
}
現在有或沒有專業化,以下代碼:
auto a = foo(1);
auto b = foo(1.0);
std::cout << std::is_same<decltype(a), int>::value << std::endl;
std::cout << std::is_same<decltype(b), double>::value << std::endl;
將打印
1
1
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.