为什么模板专业化不能改变返回类型?

时间:2016-08-06 13:57:15

标签: c++ templates

阅读this question后,我不得不再次意识到我对模板的了解程度。我可以理解,像这样的模板专业化

// 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不可能的原因是什么?

3 个答案:

答案 0 :(得分:4)

即使很奇怪,你也可以

template <typename T>
void foo(int);

template <typename T>
char foo(int);

Demo

所以你的专业化将是模棱两可的。

答案 1 :(得分:3)

实际上,您可以通过使用返回类型的模板参数或特征类来解决它 举个例子:

foo_brackets_json['KEY2']['KEY2b']

#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, "!"); } 的情况下,如果推断出参数,那么两个看似相同的调用具有不同的返回类型。

也就是说,专业化不允许用户更改功能声明 这就是为什么你必须使用这样的定义来为不同的模板参数设置不同的返回类型。

答案 2 :(得分:0)

1。默认模板参数

您的案例&#34; C&#34;可以使用默认模板参数轻松解决:

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);

2。输入地图

另一种方法更加丑陋,但更为普遍。它将要求您在一个位置枚举所有可能的返回类型,允许您根据模板参数类型选择任何返回类型。这个解决方案的关键只是一个编译时类型的映射。它可以使用对,元组和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;
};

地图&#34;返回&#34;给定密钥类型的值类型,如果未找到密钥,则为int。有了这个地图,让我们声明一个返回类型,它取决于一个模板参数:

template<typename key>
using return_type = typename type_map<key, double, double>::type;

最后你的例子&#34; C&#34;将再次通过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