如何正确设计和实现函数模板特化?

How to design and implement function template specialization properly?

我想实现 std::ifstream 的一些读取功能。 它需要区分 Pod 类型和其他类型。 (目前 std::string

template <typename T, typename = std::enable_if<std::is_pod<T>::value>::type>
T read(std::ifstream& fin);

template <>
std::string read<std::string, void>(std::ifstream& fin);

int main()
{
    std::ifstream fin("test", std::ios::binary);
    int x = read<int>(fin);
    std::string str = read<std::string, void>(fin);
}

我想在为 std::string 调用读取时从模板参数中删除“void”。 我怎样才能得到它?

提前致谢。


更新(2017/09/14)

我得到了 EC++ 的提示,并尝试实现以下代码。

    template <bool B>
    struct is_fundamental {
        enum { value = B };
    };

    template <typename T>
    static T doRead(std::ifstream& fin, is_fundamental<true>);

    template <typename T>
    static T doRead(std::ifstream& fin, is_fundamental<false>);

    template <>
    static std::string doRead<std::string>(std::ifstream& fin, is_fundamental<false>);

    template <typename T>
    static T read(std::ifstream& fin) {
        return doRead<T>(fin, is_fundamental<std::is_fundamental<T>::value>());
    }


    int main()
    {
        std::string filename("./test.dat");
        std::ifstream fin(filename, std::ios::binary);

        read<int>(fin);
        read<std::string>(fin);
        read<std::vector<int>>(fin);

        return 0;
    }

每次读取<>调用都获得正确的功能!

问题是无法部分特化一个函数。

结构体的使用呢?

如果您编写 read 广告,

template <typename T>
struct read 
 {
   template <typename U = T>
   typename std::enable_if<std::is_same<U, T>::value 
                        && std::is_pod<T>::value, T>::type
      operator() (std::ifstream & fin)
    { /* do something; return a T */ }
 };

template <>
struct read<std::string>
 {
   std::string operator() (std::ifstream & fin)
    { /* do something; return a string */ }
 };

你有一个通用版本的 read 结构,其中 operator() 仅在模板类型为 POD 的情况下启用,而 std::string 的专用版本(并且您可以添加 read 的其他特化;也可以添加部分特化)。

缺点是你必须这样改变read()的调用

int x = read<int>{}(fin);
std::string str = read<std::string>{}(fin);

即定义一个对象(read<T>{}).

如果您更喜欢在读取中使用静态成员——例如,func()——您可以避免创建对象的需要,但您必须以这种方式调用它

int x = read<int>::func(fin);
std::string str = read<std::string>::func(fin);

以下是完整的工作示例

#include <vector>
#include <fstream>
#include <iostream>
#include <type_traits>

template <typename T>
struct read 
 {
   template <typename U = T>
   typename std::enable_if<std::is_same<U, T>::value 
                        && std::is_pod<T>::value, T>::type
      operator() (std::ifstream & fin)
    { T ret ; std::cout << "POD!" << std::endl ; fin >> ret ; return ret; }
 };

template <>
struct read<std::string>
 {
   std::string operator() (std::ifstream & fin)
    { std::string ret ; std::cout << "string!" << std::endl; fin >> ret ;
      return ret; }
 };

int main()
 {
   std::ifstream fin("test", std::ios::binary);
   int x = read<int>{}(fin);                    // write POD!
   std::string str = read<std::string>{}(fin);  // write string!
   //auto read<std::vector<int>>{}(fin);        // compilation error
 }

作为替代方案,您可以使用重载和标记调度:

template <typename> struct Tag {};

template <typename T, std::enable_if_t<std::is_pod<T>::value>* = nullptr>
T read(Tag<T>, std::ifstream& fin);

std::string read(Tag<std::string>, std::ifstream& fin);

并使用它:

int main()
{
    std::ifstream fin("test", std::ios::binary);
    int x = read(Tag<int>{}, fin);              // write POD!
    auto str = read(Tag<std::string>{}, fin);   // write string!
    //auto v = read(Tag<std::vector<int>>{}, fin); // compilation error
}