模板类型不是使用指针类型推断的

时间:2015-03-25 00:05:25

标签: c++ templates

我很惊讶地发现在以下代码中无法成功推断出T

template <typename T>
void Set(T* a,T b)
{
    *a = b;
}

void Test()
{
    unsigned long a;
    Set(&a, 1);
}

VC ++抱怨含糊不清:

1>test.cpp(10): error C2782: 'void Set(T *,T)' : template parameter 'T' is ambiguous
1>          test.cpp(32) : see declaration of 'Set'
1>          could be 'int'
1>          or       'unsigned long'

显然,可以通过将呼叫更改为Set(&a, 1ul);来解决此问题,但我的问题是为什么这是必要的?

为什么不能使用指针的类型来推导T,这应该是明确的?

有没有办法重写模板,以便对Set的原始调用成功编译?

4 个答案:

答案 0 :(得分:6)

  

为什么不能使用指针的类型推导出T,这应该是   不含糊?

因为您没有告诉编译器这样做。模板参数的扣除不一致会导致扣减失败。

  

有没有办法重写模板以便原始调用   Set会成功编译吗?

是的,使用非推断的上下文。

template <typename T>
struct identity {using type=T;};
template <typename T>
using identity_t = typename identity<T>::type;

template <typename T>
void Set(T* a, identity_t<T> b) {
    *a = b;
}

Demo
或者使用第二个模板参数。

答案 1 :(得分:3)

参数T* aT b都是推导出的上下文,因此首先编译器会尝试从它们中推导出T,以便T* 第一个参数的类型相同,T 相同,与第二个参数的类型相同(遵循通常的衰减调整)。这从第一个参数产生unsigned long,从第二个参数产生int,因此这种推导尝试失败。

然后编译器将尝试查找T,使第一个参数可转换为 T*,第二个参数可转换为 { {1}}(而不是类型相同)。但是,在这种情况下,只考虑某些转换。参见N3936中的[temp.deduct.call],

  

(4)通常,演绎过程会尝试查找模板参数值,以便推导出T   与A相同(在如上所述转换类型A之后)。但是,有三种情况允许   差异:

     

(4.1) - 如果原始A是引用类型,则推导出的P(即引用所指的类型)可以是   比转换后的A更符合cv资格。

     

(4.2) - 转换后的A可以是另一个指向成员类型的指针或指针,可以转换为   通过资格转换(4.4)推断出A

     

(4.3) - 如果A是一个类而P的格式为 simple-template-id,,则转换后的P可以是派生的一类   推导出的A。同样,如果A是指向 simple-template-id形式的类的指针,则转换后的P   可以是指向推导出的A指向的派生类的指针。

     

(5)只有在类型扣除失败的情况下才考虑这些替代方案。如果他们产生不止一个   可能推导出A,类型推导失败。 [注意:如果在任何一个中没有使用模板参数   函数模板的函数参数,或者仅在非推导的上下文中使用,其对应   无法从函数调用中推断出 template-argument ,并且必须明确 template-argument   指定。 - 结束记录]

对于推导上下文中的参数类型,不考虑整数转换,例如Aint。因此,unsigned long在这种情况下也不可能是T。因此,unsigned long的推断完全失败。

我的建议是:

T

答案 2 :(得分:0)

它含糊不清,因为它可能都是

  • int*int(因为1是int
  • unsigned long*unsigned long(因为您的指针是unsigned long*

答案 3 :(得分:0)

值1推导为类型int,与&a推导出的模板参数不匹配。

  

有没有办法重写模板以便原始调用   设置将成功编译

两种参数方式:(仅适用于完整性

#include <type_traits>
// SFINAE : in case S is not convertible to T
template <typename T, typename S,
         typename std::enable_if<
         std::is_convertible<S, T>::value>::type* = nullptr >
void Set(T *a, S b) 
{ 
   *a = b; 
}