簡體   English   中英

函數模板重載vs顯式特化

[英]Function template overloading vs Explicit specialization

我設置了兩個模板函數,它們為不同的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;
}

兩者都是顯式的特化語法

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

對於具體類型,您最好只是定義一個非模板重載:

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>

我沒有完全理解你的問題,因為你的代碼似乎沒問題,但我會嘗試回答。 當您手動編寫具有相同名稱但不同參數類型的多個函數時,函數重載是一種方法。 例如:

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所提到的那樣,專業化可能對你的案件來說太過分了; 非模板重載應該足夠了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM