未使用指针类型推导出模板类型

Template type not deduced using pointer type

本文关键字:类型 指针 未使用      更新时间:2023-10-16

我惊讶地发现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的原始调用能够成功编译?

为什么不能使用指针的类型来推导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;
}

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

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

然后编译器将尝试找到T,使得第一个参数可转换为T*,而第二个参数可转换至T(而不是类型相同)。但是,在这种情况下,只考虑某些转换。参见N3936,中的[临时扣除呼叫]

(4) 通常,推导过程试图找到模板参数值,这些值将使推导出的A与CCD_ 16相同(在如上所述对类型CCD_。然而,有三种情况允许区别:

(4.1)——如果原始P是引用类型,则推导出的A(即引用引用的类型)可以是比变换后的CCD_ 20更符合cv。

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

(4.3)-如果P是一个类,并且P的形式为简单模板id,,则变换后的A可以是推导出的CCD_ 26。同样,如果P是指向简单模板id形式的类的指针,则转换后的A可以是指向由推导的CCD_ 29所指向的派生类的指针。

(5) 只有在类型推导失败的情况下,才会考虑这些备选方案。如果它们产生不止一个可能推导出的A,则类型推导失败。[注意:如果在函数模板的函数参数,或仅在非推导上下文中使用,其对应模板参数不能从函数调用中推导出来,并且模板参数-尾注]

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

我的修复建议:

template <typename T, typename U>
void Set(T* a, U&& b)
{
    *a = std::forward<U>(b);
}

这是不明确的,因为它可能既是

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

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

有没有一种方法可以重写模板,使原始调用到Set将成功编译

双参数方式:(仅用于完整性

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