函数模板重载vs显式特化

时间:2016-09-15 07:02:57

标签: c++ templates stl

我设置了两个模板函数,它们为不同的STL容器获取总和:一个用于列表和向量,另一个用于映射。

请参阅第二个模板函数定义的注释行(1)和(2)。注释掉的代码(= 2)也可以正常工作,所以我不知道哪一个是更推荐的语法。

此外,每个方法如何调用(我在评论中恰当地猜到了)?要说(1)是一个函数模板重载似乎不够,因为它在关键字'template'之后缺少typename参数。也就是说,它应该像template<typename T>结合(1),以便将方法作为函数模板重载调用,我猜。请给我他们正确的名字。

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

3 个答案:

答案 0 :(得分:1)

两者都是显式专业化语法

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

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

第一个让编译器推导出参数,而第二个让你明确。

当编译器无法推断出

的所有模板参数时,第二个是必需的
template <typename T> std::string getNameType();

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

或消除歧义哪个模板功能专门化

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

通常最好使用重载而不是函数的特化,因此对于您的示例:

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

答案 1 :(得分:0)

对于具体类型,您最好只是定义一个非模板重载:

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

模板对于通用函数更有用:

//
// 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;
}

...或者迂腐一般(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;
}

现在很高兴总结:

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

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

答案 2 :(得分:0)

我没有完全理解你的问题,因为你的代码似乎没问题,但我会尝试回答。当您手动编写具有相同名称但不同参数类型的多个函数时,函数重载是一种方法。例如:

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

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

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

在您的示例中,您编写了一个函数模板:

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

和模板专业化:

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

哪个更好?这取决于你的情况。看看,有了函数重载,你应该自己编写代码,而在模板的情况下,编译器会为你做这些!

例如,您的一般案例模板适用于vectorlistqueuedeque以及任何其他可能暂时不存在的兼容容器模板创建。编译器仅为那些用于实例化模板的类型生成代码。如果您尝试使用不兼容的类型对其进行实例化,则会出现编译错误。并且只有使用map<string, double>实例化它(对于一般案例模板无效),编译才会成功,因为将选择专门化来生成代码。

正如@RichardHodges所提到的,专业化可能对你的案件来说太过分了;非模板重载应该足够了。