2017-09-12 128 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) { ... }