2017-09-12 165 views
0

我有一組其接收的索引(在示例的int)模板函數和返回給定的類型的值,我使用SFINAE從算術類型分開std::string爲什麼在此函數模板中替換失敗?

// 1 
template <typename T> 
typename std::enable_if<std::is_arithmetic<T>::value, T>::type 
t(int) { ... } 

// 2 
template <typename T> 
typename std::enable_if<std::is_same<std::string, T>::value, T>::type 
t(int) { ... } 

// 3 
template <template <typename ...> class T, typename ... P> 
T<P ...> t(int) { ... } 

另外,有接收容器中,並使用上述功能,填補它的功能:

template <typename C> 
C c(int) 
{ 
    C r{}; 
    std::insert_iterator<C> iterator(r, r.begin()); 
    *iterator = t<typename C::value_type>(0); 
    return r; 
} 

t目標是分辨,但是如果提供了一對(因爲它來自關聯容器),那麼,t應該將第一個和第二個類型的兩個不同的t調用中的每個對分割。

雖然反序列化它的工作原理的非關聯容器,但使用關聯容器編譯失敗:

using vi = std::vector<int>; 
using mii = std::map<int, int>; 

auto o = c<vi>(0); // Deserialize vector 
auto p = c<mii>(0); // Deserialize map 

彙編在反序列化容器的一個元件的點失敗:

*iterator = t<typename C::value_type>(0); 

對於非關聯容器C::value_type是一種類型,其中前兩個版本的條件之一是t,但對於關聯容器C::value_type是一對,並且在版本#1和# 2的t但不適用於#3版本的t函數;問題是,它無法爲他們三人:

error: no matching function for call to 't' 
*iterator = t<typename C::value_type>(0); 
      ^~~~~~~~~~~~~~~~~~~~~~~~~ 
note: in instantiation of function template specialization 'c<std::map<int, int>>' requested here 
auto p = c<mii>(0); 
     ^
note: candidate template ignored: requirement 'std::is_arithmetic<pair<const int, int> >::value' was not satisfied [with T = std::pair<const int, int>] 
t(int) { ... } 
^ 
note: candidate template ignored: requirement 'std::is_same<std::string, pair<const int, int> >::value' was not satisfied [with T = std::pair<const int, int>] 
t(int) { ... } 
^ 
note: candidate template ignored: invalid explicitly-specified argument for template parameter 'T' 
T<P ...> t(int) { ... } 
     ^

顯然,編譯器抱怨缺少的模板,模板參數,但是,如果我擺脫SFINAE的錯誤消失:

template <typename T> 
T 
t(int) { return {}; } 

template <template <typename ...> class T, typename ... P> 
T<P ...> t(int) { return {}; } 

template <typename C> 
C c(int) 
{ 
    C r{}; 
    std::insert_iterator<C> iterator(r, r.begin()); 
    *iterator = t<typename C::value_type>(0); 
    return r; 
} 

int main() 
{ 
    using vi = std::vector<int>; 
    using mii = std::map<int, int>; 

    auto o = c<vi>(0); 
    auto p = c<mii>(0); 

    // print 0 
    for (auto &v : o) std::cout << v << '\n'; 
    // print 00 
    for (auto &v : p) std::cout << v.first << v.second << '\n'; 

    return 0; 
} 

它看起來像SFINAE強制模板參數需要而不是推斷,爲什麼會發生這種情況?我應該如何解決它?

代碼可在Wandbox 三へ(へ՞ਊ ՞)へ ハッハッ

+0

那麼最初的't'有兩個模板參數,新的't'只有1.你必須使用't '來調用原始的't'。 'std :: pair '不是模板模板參數的有效參數。 – Simple

+1

關聯容器的'value_type'通常是一個鍵/值對。關聯容器提供'mapped_type'。 –

+0

@簡單的說,如果我擺脫了SFINAE的混亂,'t'不需要指定參數([檢查出來](https://wandbox.org/permlink/Dj96G6V15bBT2c3S))推斷出這對。 –

回答

2

它看起來像(從您的評論和編輯),您希望根據給定的模板參數執行不同的功能。最簡單的方法是使用一個類,因爲類在專業化方面更加靈活。這裏是你可以做一個小例子:

// initial declaration (without definition), the second template 
// parameter will be used to enable some specializations 
template <class T, class = void> 
struct deserializer; 

// specialization for arithmetic types 
template <class T> 
struct deserializer< 
    T, std::enable_if_t<std::is_arithmetic<T>::value>> { 

    T operator()() const { 

    } 
}; 

// specialization for std::string 
template <> 
struct deserializer<std::string> { 
    std::string operator()() const { 

    } 
}; 

// specialization for std::pair<U, V> 
template <class U, class V> 
struct deserializer<std::pair<U, V>> { 
    std::pair<U, V> operator()() const { 

    } 
}; 

然後在你的函數c

deserializer<typename C::value_type> ds; 
*iterator = ds(); 

您還可以,如果你不希望創建的對象添加一箇中間的通用功能鍵入deserializer每次:

template <class T> 
T deserialize() { 
    return deserializer<T>{}(); 
} 

但我覺得在這裏你的目標是反序列化多個對象,所以有一個仿函數是不是在這種情況下,說不好。


爲什麼扣除在你的情況下失敗?

實際上,這裏沒有扣除,因爲扣除與參數一起使用,並且您正在使用返回類型。這裏的問題是,這個實例的t

t<std::pair<int, int>> 

...永遠比不上這個聲明的t

template <template <class... > class, class... > 
auto t(); 

因爲你將需要:

t<std::pair, int, int> 

...以匹配這樣的模板簽名。可能使用t<typename C::value_type>匹配的唯一模板簽名形式的簽名:

template <class T, /* something */> 

...其中/* something */或者是一個可變參數模板參數(class...),或默認的模板參數(class X = void的列表,int N = 0 )或兩者的組合。

1

這裏的問題是,原來t和新t有不同的模板參數:

// original. 
template <template <typename ...> class T, typename ... P> 
T<P ...> t(int) { ... } 

// new. 
template <typename C> 
C c(int) 

注意不僅原來的t有(可能)超過1個模板參數,但第一個參數是模板模板參數,而不是類型參數。

對於模板參數推導你似乎也感到困惑。模板參數推導從函數參數中推導模板參數。您的所有功能都有一個參數int,因此不會發生扣除。

換句話說,t<typename C::value_type>(0)無法使用原始功能,因爲std::pair<const int, int>不是有效的模板模板參數。您需要編寫t<std::pair, const int, int>(0)

如果你的問題是如何使用SFINAE接受「容器」(不是真的,因爲容器可以有非類型模板參數),那麼這應該工作:

template<typename T> 
struct is_container : std::false_type { }; 

template<template<typename...> class C, typename... Ts> 
struct is_container<C<Ts...>> : std::true_type { }; 

template <typename T> 
typename std::enable_if<is_container<T>::value, T>::type 
t(int) { ... }