简体   繁体   English

函数模板重载vs显式特化

[英]Function template overloading vs Explicit specialization

I set up two template functions which get sums for different STL containers: one is for list and vector, the other is for the map. 我设置了两个模板函数,它们为不同的STL容器获取总和:一个用于列表和向量,另一个用于映射。

Please see the commented line (1) and (2) of the second template function definition. 请参阅第二个模板函数定义的注释行(1)和(2)。 The commented-out code (=2) also works fine, so I do not know which one is more recommended syntax. 注释掉的代码(= 2)也可以正常工作,所以我不知道哪一个是更推荐的语法。

Also, how does each method called (Am I aptly guessed in my comment)? 另外,每个方法如何调用(我在评论中恰当地猜到了)? To say (1) is a function template overloading seems not enough because it lacks typename argument after the keyword 'template'. 要说(1)是一个函数模板重载似乎不够,因为它在关键字'template'之后缺少typename参数。 That is, It should be like template<typename T> combined with (1) in order for the method to be called as a function template overloading, I guess. 也就是说,它应该像template<typename T>结合(1),以便将方法作为函数模板重载调用,我猜。 Please give me their right name. 请给我他们正确的名字。

template <typename T> // T : container
double Sum(const T &l) // get Container
{
    double sum = 0;
    T::const_iterator i;
    for (i = l.begin(); i != l.end(); ++i) { sum += *i; }
    return sum;
}

template <> // get container
double Sum(const map<string, double> &m) // (1) function template overloading
// double Sum<map<string, double> >(const map<string, double> &m) // (2) explicit specialization
{
    double sum = 0;
    map<string, double>::const_iterator i; // obtain Iterator from Container
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data)
    return sum;
}

Both are explicit specialization syntax 两者都是显式的特化语法

  • template <> double Sum(const map<string, double> &m);

  • template <> double Sum<map<string, double> >(const map<string, double> &m)

The first one let compiler deduce the parameter, whereas the second, you are explicit. 第一个让编译器推导出参数,而第二个让你明确。

The second is required when compiler cannot deduced all template parameters as for 当编译器无法推断出所有模板参数时,第二个是必需的

template <typename T> std::string getNameType();

template <> std::string getNameType<int>() { return "int"; }

or to disambiguate which template function to specialize 或消除模糊哪个模板功能专门化

template <typename T> void foo(T);

template <typename T> void foo(T*); // overload, not specialization

//template <> void foo(int*); // Error: do you mean T = int for foo(T*) or T = int* for foo(T)

template <> void foo<int*>(int*); // specialize foo(T)
template <> void foo<int>(int*);  // specialize foo(T*)

It is generally better to use overload instead of specialization for function, so for your example: 通常最好使用重载而不是函数的特化,因此对于您的示例:

template <typename Key>
double Sum(const std::map<Key, double> &m)
{
    double sum = 0;
    for (const auto& p : m) { sum += p.second; }        return sum;
}

For a concrete type, you're probably better off simply defining a non-template overload: 对于具体类型,您最好只是定义一个非模板重载:

double Sum(const std::map<std::string, double> &m) // (1) function template overloading
// double Sum<map<string, double> >(const map<string, double> &m) // (2) explicit specialization
{
    double sum = 0;
    std::map<std::string, double>::const_iterator i; // obtain Iterator from Container
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data)
    return sum;
}

Templates are more useful for generic functions: 模板对于通用函数更有用:

//
// sum any map's arithmetic mapped values
//
template<class K, class V, class C, class A>
typename std::map<K, V, C, A>::mapped_type
Sum(const std::map<K, V, C, A> &m) // (1) function template overloading
{
    using map_type = std::map<K, V, C, A>;
    using mapped_type = typename map_type::mapped_type;
    using const_iterator = typename map_type::const_iterator;
    mapped_type sum = 0;
    const_iterator i; // obtain Iterator from Container
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data)
    return sum;
}

... or to get pedantically generic (c++14)... ...或者迂腐一般(c ++ 14)......

namespace notstd {
    template< class... >
    using void_t = void;
}

template< class, class = notstd::void_t<> >
struct supports_mapped_type : std::false_type { };

template< class T >
struct supports_mapped_type<T, notstd::void_t<typename T::mapped_type>> : std::true_type { };

template< class, class = notstd::void_t<> >
struct supports_const_iterator : std::false_type { };

template< class T >
struct supports_const_iterator<T, notstd::void_t<typename T::const_iterator>> : std::true_type { };

template<class T> static constexpr bool IsMaplike = supports_mapped_type<T>() and supports_const_iterator<T>();

template<class MapLike,
std::enable_if_t<
IsMaplike<MapLike> and std::is_arithmetic<typename MapLike::mapped_type>()
>* = nullptr>
typename MapLike::mapped_type
Sum(const MapLike &m) // (1) function template overloading
{
    using map_type = MapLike;
    using mapped_type = typename map_type::mapped_type;
    using const_iterator = typename map_type::const_iterator;
    mapped_type sum = 0;
    const_iterator i; // obtain Iterator from Container
    for (i = m.begin(); i != m.end(); i++) { sum += i->second; } // sum. ('first' is index, 'second' is data)
    return sum;
}

This would now happily sum: 现在可以很高兴地总结:

std::unordered_map<std::string, int> , and std::map<std::string, int> , std::unordered_map<std::string, int>std::map<std::string, int>

but not std::map<int, std::string> 但不是std::map<int, std::string>

I didn't completely understand your question, as your code seems to be OK, but I'll make an attempt to answer. 我没有完全理解你的问题,因为你的代码似乎没问题,但我会尝试回答。 Function overloading is an approach when you manually write several functions with the same name but different argument types. 当您手动编写具有相同名称但不同参数类型的多个函数时,函数重载是一种方法。 For example: 例如:

double Sum(const std::vector<double>& l) {
    //...
}

double Sum(const std::list<double>& l) {
    //...
}

double Sum(const std::deque<double>& l) {
    //...
}

In your example you wrote a function template: 在您的示例中,您编写了一个函数模板:

template <typename T>
double Sum(const T &l) //...

and a template specialization: 和模板专业化:

template <>
double Sum(const map<string, double> &m) //...

Which is better? 哪个更好? It depends on your situation. 这取决于你的情况。 See, with function overloading you should write the code by yourself, while in case of templates the compiler will do it for you! 看看,有了函数重载,你应该自己编写代码,而在模板的情况下编译器会为你做!

For example, your general-case template will work for vector , list , queue , deque and any other compatible container which even may be nonexistent for the moment of template creation. 例如,您的通用案例模板将适用于vectorlistqueuedeque和任何其他兼容容器,这些容器在创建模板时甚至可能不存在。 Compiler generates the code only for those types which are used to instantiate the template. 编译器仅为那些用于实例化模板的类型生成代码。 If you try to instantiate it with incompatible type, you'll get a compilation error. 如果您尝试使用不兼容的类型对其进行实例化,则会出现编译错误。 And only if you instantiate it with map<string, double> (which is invalid for the general case template), compilation will succeed as the specialization will be selected for code generation. 并且只有使用map<string, double> (对于一般案例模板无效)实例化它,编译才会成功,因为将选择特化来生成代码。

As @RichardHodges mentioned, specialization might be an overkill for your case; 正如@RichardHodges所提到的那样,专业化可能对你的案件来说太过分了; non-template overloading should be enough. 非模板重载应该足够了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM