身份别名模板可以是转发引用吗? [英] Can an identity alias template be a forwarding reference?

查看:136
本文介绍了身份别名模板可以是转发引用吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请考虑以下代码段:

 模板< class T& 
using identity = T;

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

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

i lvalue,因此如果 foo 声明转发引用参数,它应该编译。然而,如果 identity< T>&& 被转换为 int&&< / code>



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



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

 错误: 'int'
to'identity< int> &&'(a> int&&')第一个参数

解决方案

请考虑以下代码:

 code> template< class T>使用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 是类模板,它的形式可以匹配函数参数的类型,而不需要查看它的定义,允许编译器推导出<$ c的模板参数$ c> T 。但是,这里我们有一个别名模板; T 无法推导至 int int& else除非 identity< T> T 替换。否则,我们匹配什么?一旦替换完成,函数参数就成为一个转发引用。



上述所有支持 identity< T& ; (和身份< T&&> )被视为等同于转发引用。



但是,似乎还有更多的要立即替换别名模板id与相应的type-id。段落[14.5.7p3]说:


但是,如果template-id是dependent,后续模板参数
替换仍然适用到模板标识。 [示例:

 模板< typename ...>使用void_t = void; 
template< typename T> void_t< typename T :: foo> F();
f< int>(); //错误,int没有嵌套类型foo

-end示例]


这似乎与你的例子没什么关系,但它实际上表明模板id的初始形式仍然被考虑在在一些情况下,独立于取代类型id。我猜这可能会导致身份< T>&& 实际上不能作为转发参考。



这个区域在标准中似乎不足。这显示了在处理类似问题的开放问题的数量,在我看来在同一类别:在什么情况下应该初始形式的template-id在实例化时考虑,即使它应该被替换为对应的type-id遇到时立即。请参阅问题 1980 2021 2025 。甚至问题 1430 1554 可能被视为处理类似问题。



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

 模板< typename T,typename U>使用X = T; 
template< typename T> X template< typename T> X F();

附注:


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


(CWG-核心工作组)



类似的推理可能适用于您的示例,使 identity< T>&& 参考。这甚至可以有实际的价值,作为避免转发引用的贪婪的一个直接的方法,当你想要的是一个推定的T的值的引用。



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



在我看来,你的问题的答案是, stackoverflow.com/q/29867841/4326278\">问题, Piotr S. 指出问题1700 ,被关闭为不是缺陷。它指的是该问题中描述的非常类似的情况,并且包含以下理由:


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


我认为以及这里讨论的案例,并解决现在的问题:所有这些形式应该被视为等同于转发参考。



(这将是有趣的看看这是否由其他开放性问题的决议间接改变,但他们主要处理替换失败,而不是自己扣除,所以我想这种间接效果是不太可能的。)






所有的标准引用都是目前的工作草案,N4431,最后的C ++ 14之后的第二个草案。



请注意,来自[14.5.7p3]的引用是最近的添加,包括在最后的C ++ 14版本之后,作为 DR1558 。我认为我们可以期待在这一领域进一步增加,因为其他问题以某种方式解决。



在此之前,可能值得在< a href =https://groups.google.com/a/isocpp.org/forum/#!forum/std-discussion =nofollow> ISO C ++标准 - 讨论群组;这应该引起正确的人的注意。


Consider the following snippet below:

template <class T>
using identity = T;

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

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

i is an lvalue, hence if foo declares a forwarding reference parameter, it should compile. However, if identity<T>&& is turned to be int&&, it should raise an error instead.

The code compiles in GCC 6.0.0 (demo).

The code fails to compile in Clang 3.7.0 (demo) with error message:

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

Which one is right?

解决方案

Consider this code:

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

Both GCC and Clang reject it because #2 is a redefinition of #1. If they're actually the same template, we could expect #1 to behave in the exact same way as #2, meaning that identity<T>&& should act as a forwarding reference. Following this logic, we don't know which one is right, but GCC is at least consistent.

This is also consistent with a very similar example in the standard at [14.5.7p2].

We should also consider the way template argument deduction can work in this case. If identity were a class template, its form could be matched against the type of the function argument without looking at its definition, allowing the compiler to deduce the template argument for T. However, here we have an alias template; T cannot be deduced to int or int& or anything else unless identity<T> is replaced by T. Otherwise, what are we matching against? Once the replacement is done, the function parameter becomes a forwarding reference.

All of the above supports the idea of identity<T>&& (and identity<T&&>) being treated as equivalent to a forwarding reference.

However, it seems that there's more to this that the immediate replacement of the alias template-id with the corresponding type-id. Paragraph [14.5.7p3] says:

However, if the template-id is dependent, subsequent template argument substitution still applies to the template-id. [ Example:

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 ]

This might not seem to have much to do with your example, but it actually indicates that the initial form of the template-id is still taken into account in some cases, independently of the substituted type-id. I guess this opens the possibility that identity<T>&& could actually not be treated as a forwarding reference after all.

This area seems to be underspecified in the standard. This shows in the number of open issues dealing with similar problems, all in the same category in my opinion: in what cases should the initial form of the template-id be taken into account upon instantiation, even though it's supposed to be replaced by the corresponding type-id immediately when encountered. See issues 1980, 2021 and 2025. Even issues 1430 and 1554 could be seen as dealing with similar problems.

In particular, issue 1980 contains the following example:

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();

with the note:

CWG felt that these two declarations should not be equivalent.

(CWG - the Core working group)

A similar line of reasoning could apply to your example, making identity<T>&& not equivalent to a forwarding reference. This could even have practical value, as a straightforward way of avoiding the greediness of a forwarding reference when all you want is an rvalue reference to a deduced T.

So, I think you've raised a very interesting problem. Your example may be worth adding as a note to issue 1980, to make sure this is taken into account when drafting the resolution.

In my opinion, the answer to your question is, for now, a resounding "who knows?".


Update: In the comments to the other, related, question, Piotr S. pointed out issue 1700, which was closed as "not a defect". It refers to the very similar case described in that question, and contains the following rationale:

Because the types of the function parameters are the same, regardless of whether written directly or via an alias template, deduction must be handled the same way in both cases.

I think it applies just as well to the cases discussed here, and settles the issue for now: all these forms should be treated as equivalent to a forwarding reference.

(It will be interesting to see if this is changed indirectly by the resolutions for the other open issues, but they mostly deal with substitution failures rather than deduction by itself, so I guess such an indirect effect is rather unlikely.)


All standard references are to the current working draft, N4431, the second draft after final C++14.

Note that the quote from [14.5.7p3] is a recent addition, included right after the final C++14 version as the resolution of DR1558. I think we can expect further additions in this area as the other issues are resolved in one way or another.

Until then, it may be worth asking this question in the ISO C++ Standard - Discussion group; that should bring it to the attention of the right people.

这篇关于身份别名模板可以是转发引用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆