身份别名模板可以作为转发参考吗?

时间:2015-04-24 15:09:51

标签: c++ templates alias c++14 forwarding-reference

请考虑以下代码段:

template <class T>
using identity = T;

template <class T>
void foo(identity<T>&&) {}

int main()
{
  int i{};
  foo(i);
}

i是一个左值,因此如果foo声明转发引用参数,它应该编译。但是,如果identity<T>&&变为int&&,则应该引发错误。

代码在GCC 6.0.0(demo)中编译。

代码无法在Clang 3.7.0(demo)中编译,并显示错误消息:

error: no known conversion from 'int' 
to 'identity<int> &&' (aka 'int &&') for 1st argument

哪一个是对的?

2 个答案:

答案 0 :(得分:8)

考虑以下代码:

template<class T> using identity = T;

template<class T> void foo(identity<T>&&) { } //#1

template<class T> void foo(T&&) { } //#2

int main()
{
  int i{};
  foo(i);
}

GCC和Clang都拒绝它,因为#2是对#1的重新定义。如果它们实际上是相同的模板,我们可以期望#1的行为方式与#2完全相同,这意味着identity<T>&&应该充当转发参考。遵循这个逻辑,我们不知道哪一个是正确的,但GCC至少是一致的。

这也与[14.5.7p2]标准中非常相似的例子一致。

我们还应该考虑模板参数推导在这种情况下可以起作用的方式。如果identity是类模板,则可以将其形式与函数参数的类型进行匹配,而不查看其定义,从而允许编译器推导出T的模板参数。但是,这里有一个别名模板;除非T替换int,否则无法将int&推断为identity<T>T或其他任何内容。否则,我们匹配什么?替换完成后,函数参数将成为转发参考。

以上所有都支持将identity<T>&&(和identity<T&&>)视为等同于转发参考的想法。

但是,似乎还有更多的东西,即使用相应的type-id立即替换别名template-id。第[14.5.7p3]段说:

  

但是,如果template-id是依赖的,则后续的模板参数   替换仍然适用于template-id。 [例如:

template<typename...> using void_t = void; 
template<typename T> void_t<typename T::foo> f(); 
f<int>(); // error, int does not have a nested type foo 
     

-end example]

这似乎与您的示例无关,但它实际上表明在某些情况下仍然会考虑模板ID的初始形式,与取代的type-id无关。我想这可能会导致identity<T>&&实际上不能被视为转发参考。

该标准似乎没有明确规定。这表明处理类似问题的公开问题的数量,在我看来都属于同一类别:在什么情况下,在实例化时应该考虑模板id的初始形式,即使它应该被替换为遇到时立即对应的type-id。查看问题198020212025。即使问题14301554也可以被视为处理类似的问题。

特别是,issue 1980包含以下示例:

template<typename T, typename U> using X = T;
template<typename T> X<void, typename T::type> f();
template<typename T> X<void, typename T::other> f();

附注:

  

CWG认为这两个声明不应该相同。

(CWG--核心工作组)

类似的推理可能适用于您的示例,使identity<T>&&不等同于转发引用。这甚至可以具有实用价值,作为一种直接的方法,当你想要的只是对推导出的T的右值引用时,避免转发引用的贪婪。

所以,我认为你提出了一个非常有趣的问题。您的示例可能值得添加为issue 1980的注释,以确保在起草决议时考虑到这一点。

在我看来,你的问题的答案是,现在,一个响亮的“谁知道?”。

更新:在对其他人的评论中,相关的questionPiotr S.指出了issue 1700,其被视为“非缺陷”。它指的是该问题中描述的非常相似的案例,并包含以下基本原理:

  

因为函数参数的类型是相同的,无论是直接写还是通过别名模板,在两种情况下都必须以相同的方式处理推论。

我认为它同样适用于此处讨论的案例,并且现在解决了这个问题:所有这些表格应该被视为等同于转发参考。

(看看这是否由其他公开问题的解决方案间接改变将是有趣的,但它们主要处理替代失败而不是自身的推论,所以我猜这种间接影响是不太可能的。)< / p>

所有标准参考文献都是当前的工作草案,N4431,最终C ++ 14之后的第二稿。

请注意,[14.5.7p3]中的引用是最近添加的,包含在最终的C ++ 14版本之后,作为DR1558的分辨率。我认为我们可以期待在这个领域进一步增加,因为其他问题以这种或那种方式得到解决。

在此之前,可能值得在ISO C++ Standard - Discussion小组中提出这个问题;这应该引起合适的人的注意。

答案 1 :(得分:7)

它不是转发参考。 C ++ 14(n4140)14.8.2.1/3(强调我的):

  

...如果P是对右值引用   模板参数,参数是左值,类型“对A的左值引用”用于   A的地方类型扣除。

这是标准的一部分,它指定了转发引用的工作方式。 P是函数参数的类型,类型为&#34;对identity<T>的右值引用。&#34; identity<T>是模板参数的类型,但它本身不是模板参数,因此转发参考扣除规则不适用。

我们还可以看看14.5.7 / 2对别名模板的评价:

  

template-id 引用别名模板的特化时,它等同于关联的类型   通过将 template-arguments 替换为别名的 type-id 中的 template-parameters 获得   模板。

因此,替换别名等同于T类型,但14.8.2.1/3读取&#34;引用...模板参数,&#34;不是&#34;引用...模板参数的类型。&#34;