Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 未使用指针类型推导模板类型_C++_Templates - Fatal编程技术网

C++ 未使用指针类型推导模板类型

C++ 未使用指针类型推导模板类型,c++,templates,C++,Templates,我惊讶地发现T无法在以下代码中成功推导: template <typename T> void Set(T* a,T b) { *a = b; } void Test() { unsigned long a; Set(&a, 1); } 显然,这可以通过将调用更改为Set(&a,1ul)来解决 无效集(T*a,S-b) { *a=b; } 为什么不能使用指针的类型(应该是 明确的 因为您还没有告诉编译器这样做。模板参数的演绎不一致会导致演绎失败

我惊讶地发现
T
无法在以下代码中成功推导:

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

void Test()
{
    unsigned long a;
    Set(&a, 1);
}
显然,这可以通过将调用更改为
Set(&a,1ul)来解决
为什么不能使用指针的类型来推断
t
,指针的类型应该是明确的


有没有一种方法可以重写模板,使对
Set
的原始调用能够成功编译?

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

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

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

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

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

#包括
//SFINAE:如果S不能转换为T
模板::类型*=nullptr>
无效集(T*a,S-b)
{ 
*a=b;
}
为什么不能使用指针的类型(应该是 明确的

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

有没有办法重写模板,使原始调用 设置是否可以成功编译

是的,使用非推断上下文

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;
}
模板
结构标识{using type=T;};
模板
使用identity\u t=typename identity::type;
模板
无效集(T*a,identity\u T b){
*a=b;
}
.

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

参数
T*a
tb
都是推导上下文,因此首先编译器将尝试从这两个参数中推导
T
,以使
T*
与第一个参数的类型相同,而
T
与第二个参数的类型相同(在通常的衰减调整之后)。这会从第一个参数中产生
无符号长
,从第二个参数中产生
int
,因此此扣减尝试失败

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

(4) 一般来说,演绎过程试图找到将演绎的
A
A
相同(在类型
A
如上所述转换之后)。但是,有三种情况允许 区别在于:

(4.1)-如果原始
p
为参考类型,则可使用推导的
a
(即参考所指的类型) 比转换后的
A
更合格的cv

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

(4.3)-如果
p
是一个类,并且
p
具有表单简单模板id,则转换后的
a
可以是 推导出的
A
。同样,如果
P
是指向表单简单模板id的类的指针,则转换后的
A
可以是指向导出的
a
所指向的派生类的指针

(5) 只有当类型扣除失败时,才会考虑这些替代方案。如果它们产生多个 可能推断出
A
,类型推断失败。[注意:如果在任何 函数模板的函数参数,或仅在非推导上下文中使用,其对应 无法从函数调用中推导模板参数,模板参数必须显式 指定-结束注释]

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

我的建议是:

template <typename T, typename U>
void Set(T* a, U&& b)
{
    *a = std::forward<U>(b);
}
模板
无效集(T*a、U和b)
{
*a=标准::正向(b);
}

1
可以是不明确的,但是
&a
不能是-它只能是
无符号长*
@JonathanPotter表达式
1
肯定是
int
类型。总是。@Columbo是的,但它可以隐式转换为
无符号长
,这是带有<代码>无符号长
参数。我不认为模板推断功能是(或应该是)可以自由推断,它是你想要的int
。这将导致更多的问题,它将实际解决。@JonathanPotter,这一点并不含糊。推断的类型仍然是int,完全不考虑int可以转换成什么。此外,以“那么编译器将[…]”开头的部分似乎不正确。在第一次尝试失败后,实现没有尝试进一步的演绎。您甚至显示了将此特殊情况排除在例外之外的引号。@Columbo我认为这纯粹是关于措辞的分歧。我将编辑一点。我要提一下
std::is\u convertible::value
的可能性,尽管我不是当然我会在这里使用它。
static\u assert
在这里是无用的,因为唯一的一行是赋值。如果您已经使用了C++11,为什么不使用decltype来表示SFINAE呢?
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;
}
template <typename T, typename U>
void Set(T* a, U&& b)
{
    *a = std::forward<U>(b);
}