针对不同类型的专业化

pre*_*eys 0 c++ templates specialization c++11

有人能告诉我如何删除下面的重复专业吗?

#include <iostream>
#include <fstream>
#include <string>

struct Thing {
    int a, b;
    void load (std::istream& is) {is >> std::skipws >> a >> b;}
};

struct Object {
    int a, b, c;
    void load (std::istream& is) {is >> std::skipws >> a >> b >> c;}
};

template <typename...> struct PassArgs;

// General case.
template <typename First, typename... Rest>
struct PassArgs<First, Rest...> : PassArgs<Rest...> {
    void operator()(std::istream& is, First& first, Rest&... rest) const {
        is >> first;
        PassArgs<Rest...>::operator()(is, rest...);
    }
};

// Specialization for std::string needed.
template <typename... Rest>
struct PassArgs<std::string, Rest...> : PassArgs<Rest...> {
    void operator()(std::istream& is, std::string& first, Rest&... rest) const {
        while (std::getline (is, first) && first.empty());
        PassArgs<Rest...>::operator()(is, rest...);
    }
};

// Specialization for class Thing.
template <typename... Rest>
struct PassArgs<Thing, Rest...> : PassArgs<Rest...> {
    void operator()(std::istream& is, Thing& first, Rest&... rest) const {
        first.load(is);
        PassArgs<Rest...>::operator()(is, rest...);
    }
};

// Specialization for class Object, but is the exact same as that for Thing.
template <typename... Rest>
struct PassArgs<Object, Rest...> : PassArgs<Rest...> {
    void operator()(std::istream& is, Object& first, Rest&... rest) const {
        first.load(is);
        PassArgs<Rest...>::operator()(is, rest...);
    }
};


template <>
struct PassArgs<> {
    void operator()(std::istream&) const {}  // End of recursion.
};


int main() {}
Run Code Online (Sandbox Code Playgroud)

一切正常,但有没有办法避免所有具有该load(std::istream&)功能的类的特化(并且我的程序中有很多).目前,我有特例Thing,Object和许多其他类,所有在他们的专长相同的路线.

顺便说一句,这是客户端使用的方式PassArgs:

template <typename T, typename... Args>
T* create (std::istream& is, Args&... args) {
    PassArgs<Args...>()(is, args...);
    T* t = new T(args...);
    // Do whatever with t;
    return t;
}
Run Code Online (Sandbox Code Playgroud)

T.C*_*.C. 6

大约有一种方法可以做到这一点.这是其中之一.

首先,检测是否有成员load()被调用的特征.这是编写它的一种方法:

namespace details {    
    template<class T>
    auto has_load_impl(int) 
         -> decltype((void)std::declval<T&>().load(std::declval<std::istream&>()),
                     std::true_type());

    template<class T>
    std::false_type has_load_impl(...);

    template<class T>
    using has_load = decltype(has_load_impl<T>(0));
}
Run Code Online (Sandbox Code Playgroud)

还有很多其他方法可以写出这个特质.例如,Jonathan Wakely的回答使用了Walter Brown的回答void_t.或者你可以使用std::experimental::is_detected.

接下来,编写一个加载单个参数的函数,根据结果调度has_load.这是一种方法:

namespace details {
    template<class T>
    void do_load(std::istream& is, T& t, std::true_type /*has_load*/){
        t.load(is);
    }

    template<class T>
    void do_load(std::istream& is, T& t, std::false_type /*has_load*/){
        is >> t;
    }
}

template<class T>
void load(std::istream& is, T& t){
    details::do_load(is, t, details::has_load<T>());
}
Run Code Online (Sandbox Code Playgroud)

do_load如果您不需要其他地方的特性,也可以直接在功能中省去独立特性和SFINAE :

namespace details {
    template<class T>
    auto do_load(std::istream& is, T& t, int) -> decltype((void)t.load(is)){
        t.load(is);
    }

    template<class T>
    void do_load(std::istream& is, T& t, ...){
        is >> t;
    }
}

template<class T>
void load(std::istream& is, T& t){
    details::do_load(is, t, 0);
}
Run Code Online (Sandbox Code Playgroud)

根据需要特殊处理的类型添加重载.

void load(std::istream& is, std::string& s){
    while (std::getline (is, s) && s.empty());
}
Run Code Online (Sandbox Code Playgroud)

最后,PassArgs使用braced-init-list中熟悉的包扩展技巧,可以简化为两行函数:

template<class... Args>
void PassArgs(std::istream& is, Args&... args){
    using expander = int[];
    (void)expander{0, (load(is, args), void(), 0)... };
}
Run Code Online (Sandbox Code Playgroud)

演示.

在上文中,用户可以load使用ADL进行定制.或者,您可以改为load创建类模板的成员函数loader<T>,并且用户可以loader根据需要进行专门化以自定义加载:

template<class T>
struct loader {
    static void load(std::istream& is, T& t) {
        details::do_load(is, t, details::has_load<T>());
    }
};

template<> struct loader<std::string>{
    static void load(std::istream& is, std::string& s){
        while (std::getline (is, s) && s.empty());
    }
};

template<class... Args>
void PassArgs(std::istream& is, Args&... args){
    using expander = int[];
    (void)expander{0, (loader<T>::load(is, args), void(), 0)... };
}
Run Code Online (Sandbox Code Playgroud)