模板类的模板函数专用化


可以在

C++11/14中写这样的东西吗?

#include <iostream>
#include <vector>
template <typename T>
T Get();
template <typename T>
struct Data {
    std::vector<T> data;
};
template <>
template <typename T>
Data<T> Get<Data<T>>() {
    return Data<T>{{T{}, T{}}};
}
template <>
template <typename T>
std::vector<T> Get<std::vector<T>>() {
    return std::vector<T>(3);
}
int main() {
    std::cout << Get<Data<int>>().data.size() << std::endl;  // expected output is 2
    std::cout << Get<std::vector<int>>().size() << std::endl; // expected output is 3
    return 0;
}

在这种情况下,重载将无济于事,因为调用Get<...>()将是模棱两可的(请参阅):

template <typename T>
Data<T> Get() {
    return Data<T>{{T{}, T{}}};
}
template <typename T>
std::vector<T> Get() {
    return std::vector<T>(3);
}

欢迎任何关于如何克服这个问题的方向。

有解决方法,它给你这样的结果: 不要专门化 - 重载:

#include <iostream>
#include <vector>
#include <string>
using namespace std;
template <typename T>
size_t Get(const T& data)
{
    return 444;
}
template <typename T>
struct Data
{
    std::vector<T> data;
};
template <typename T>
size_t Get(const Data<T>& data) {
    return data.data.size();
}
int main() {
    std::cout << Get<>(0) << std::endl;  // expected output is 444
    std::cout << Get<>(Data<int>{}) << std::endl;  // expected output is 0
    return 0;
}

输出:

444
0

请注意,size_t Get(const Data<T>& data) 不是一个专业化 - 它是完全"不同"的Get(),对于任何T,都需要用于类型 Data<T> 的参数。

在这里您可以看到工作示例。


编辑

我看到你完全改变了你的问题。但是,我仍然会尝试回答它。对于缺少部分函数专用化,有一个标准的解决方法 - 使用对结构/类的委派。

以下是您需要的:

#include <iostream>
#include <vector>
using namespace std;
template <typename T>
struct GetImpl;
template <typename T>
struct Data {
    std::vector<T> data;
};
template <typename T>
struct GetImpl< Data<T> >
{
    static Data<T> Get() {
        return Data<T>{ {T{}, T{}} };
    };
};
template <typename T>
struct GetImpl< std::vector<T> >
{
    static std::vector<T> Get() {
        return std::vector<T>(3);
    };
};
int main() {
    std::cout << GetImpl< Data<int> >::Get().data.size() << std::endl;  // expected output is 2
    std::cout << GetImpl< std::vector<int> >::Get().size() << std::endl; // expected output is 3
    return 0;
}

输出:

2
3

工作示例可以在这里找到。


如果你不喜欢这种语法,你可以通过将静态函数Get()更改为函数调用运算符来使其缩短一点:

template <typename T>
struct Get< Data<T> >
{
    Data<T> operator()() {
        return Data<T>{ {T{}, T{}} };
    };
};
template <typename T>
struct Get< std::vector<T> >
{
    std::vector<T> operator()() {
        return std::vector<T>(3);
    };
};

然后:

Get< Data<int> >()().data.size();
Get< std::vector<int> >()().size();

您只有两个额外的字符 - () .这是我能想到的最短的解决方案。

正如 Columbo 在他的评论中提到的,您应该应用标准解决方法,因为缺乏对函数的部分专用化支持: 委派给部分专用类:

template <typename T>
struct GetImpl;
template <typename T>
T Get() { return GetImpl<T>::Do(); }

现在对struct GetImpl<T> { static T Do(); }使用部分专用化而不是Get<T>()

但是编译器不可能将Get<Data<int>>Get<Data<Data<int>>>区分开来。

这并非不可能。如果需要这样做,我们可以添加单独的重载:

template <typename T>
size_t Get(const Data<T>& data);
template <typename T>
size_t Get(const Data<Data<T>>& data); // preferred for Data<Data<int>>

或者,如果您想要只是对非嵌套情况进行重载,我们可以添加一个类型特征并使用 SFINAE:

template <typename T> struct is_data : std::false_type { };
template <typename T> struct is_data<Data<T>> : std::true_type { };
template <typename T>
enable_if_t<!is_data<T>::value, size_t>
Get(const Data<T>& data);

这样,带有Data<Data<int>>的调用将调用泛型Get(const T&)。或者,如果您希望该情况根本不编译:

template <typename T>
size_t Get(const Data<T>& data) {
    static_assert(!is_data<T>::value, "disallowed");
    ...
}

因此,重载为您提供了很多选择。专业化不会给你任何东西,因为它无论如何都是不允许的。

在委派到结构的方式之后,您可以实现更通用的方法: 您可以使用结构来检查容器类型和内部类型,如下所示:

#include <iostream>
#include <vector>
template <typename T>
struct Data {
    std::vector<T> data;
};
template <template <typename...> class Container, typename>
struct get_inner;
template <template <typename...> class Container, typename T>
struct get_inner<Container, Container<T>>
{
    typedef T type;
};


template <typename T, typename U = typename get_inner<Data, T>::type>
Data<U> Get() {
    return Data<U>{ {U{}, U{}} };
}
template <typename T, typename U = typename  get_inner<std::vector, T>::type>
std::vector<U> Get() {
    return std::vector<U>(3);
}
int main() {
    std::cout << Get<Data<int>>().data.size() << std::endl;  // expected output is 2
    std::cout << Get<std::vector<int>>().size() << std::endl; // expected output is 3
    return 0;
}

http://coliru.stacked-crooked.com/a/90b55767911eff0e

最新更新