C++ VC++;具有Decltype和通用引用或未定义行为的Bug?

C++ VC++;具有Decltype和通用引用或未定义行为的Bug?,c++,c++11,decltype,compiler-bug,C++,C++11,Decltype,Compiler Bug,我有以下代码: #include <iostream> #include <type_traits> using namespace std; template<typename T> T f2(T&&t) { return t; } int rr(int i) { return 40*i; } int main() { cout << is_function< remove_reference<de

我有以下代码:

#include <iostream>
#include <type_traits>
using namespace std;
template<typename T>
T f2(T&&t) {
    return t;
}
int rr(int i) {
    return 40*i;
}
int main()
{
  cout << is_function< remove_reference<decltype(f2(rr))>::type>::value << endl;
}
主要问题是在表达式
f2(rr)
上使用
decltype()
。请注意,
f2
的参数是
T&
。这被Scott Meyers称为通用参考。我希望
f2(rr)
生成一个类型为函数引用的表达式。在GCC中,它正常运行并返回true,因此确认
f2(rr)
是一个函数引用

当univeral引用与函数名一起使用时,这仅仅是VC++2015的一个bug,而不是未定义的行为吗

编辑:这在VC++2015中正常工作:

int main()
{
  cout << f2(rr)(10) <<endl;
}

通用引用是一种特殊的上下文,在此上下文中可以将模板参数推断为引用类型。假设我们有

template <typename T> auto f(T &&) -> T;
int i;
int g(int);
模板自动f(T&)->T;
int i;
int g(int);
f(i)
T
推断为
int&
形成
autof(int&)->int&

f(std::move(i))
T
推断为
int
以形成
自动f(int&)->int

发生这种情况时的规则如下所示:

14.8.2.1从函数调用中推断模板参数[temp.Decrete.call]

3[…]如果
p
是对cv非限定模板参数的右值引用,且参数是左值,则类型推断使用“对
a
的左值引用”代替
a
。[……]

问题是,当调用
f(g)
时,
g
是左值吗

问这个问题有意义吗?如果
T
是一种函数类型,那么
T&
T&
都可以用于创建对命名函数的引用,而不需要
std::move
。函数不存在左值/右值的区别

任意,C++标准称是,<代码> g <代码>是一个LValk。函数类型的表达式从不为右值(无论是xvalues还是prvalues)。可以生成对函数类型的右值引用的类型,但即使如此,它们形成的表达式也是左值。本标准的其他地方有更明确的陈述,但它们如下所示:

3.10左值和右值[basic.lval]

(1.1)--左值[…]表示函数或对象。[……]

(1.2)--xvalue[…]也指对象[…]

(1.4)--右值[…]是一个xvalue、一个临时对象(12.2)或其子对象,或者是一个与对象无关的值

(注意:
g
也不是“值”,因此最后一部分不适用。“值”在[basic.types]p4中定义,并适用于可复制的普通类型,而函数类型不适用。)


由于
g
是一个左值,
T
应该被推断为
int(&)(int)
,这是VC++中的一个错误。不过这是一个相当微妙的问题。

通用引用是一种特殊的上下文,在这种上下文中,模板参数可以推断为引用类型。假设我们有

template <typename T> auto f(T &&) -> T;
int i;
int g(int);
模板自动f(T&)->T;
int i;
int g(int);
f(i)
T
推断为
int&
形成
autof(int&)->int&

f(std::move(i))
T
推断为
int
以形成
自动f(int&)->int

发生这种情况时的规则如下所示:

14.8.2.1从函数调用中推断模板参数[temp.Decrete.call]

3[…]如果
p
是对cv非限定模板参数的右值引用,且参数是左值,则类型推断使用“对
a
的左值引用”代替
a
。[……]

问题是,当调用
f(g)
时,
g
是左值吗

问这个问题有意义吗?如果
T
是一种函数类型,那么
T&
T&
都可以用于创建对命名函数的引用,而不需要
std::move
。函数不存在左值/右值的区别

任意,C++标准称是,<代码> g <代码>是一个LValk。函数类型的表达式从不为右值(无论是xvalues还是prvalues)。可以生成对函数类型的右值引用的类型,但即使如此,它们形成的表达式也是左值。本标准的其他地方有更明确的陈述,但它们如下所示:

3.10左值和右值[basic.lval]

(1.1)--左值[…]表示函数或对象。[……]

(1.2)--xvalue[…]也指对象[…]

(1.4)--右值[…]是一个xvalue、一个临时对象(12.2)或其子对象,或者是一个与对象无关的值

(注意:
g
也不是“值”,因此最后一部分不适用。“值”在[basic.types]p4中定义,并适用于可复制的普通类型,而函数类型不适用。)


由于
g
是一个左值,
T
应该被推断为
int(&)(int)
,这是VC++中的一个错误。这是一个相当微妙的问题。

VC++似乎将
T
推断为
int(int)
,它用于将函数传递给
T&
类型的参数,但使
T
成为无效的函数返回类型。GCC将
T
推断为
int(&)(int)
。编译器应该是正确的。在CLAN上编译好。上个月从微软Connect得到了这个响应:“谢谢报告这个问题。这个问题的解决已经被检查到编译器源。修复应该在VisualC++的未来版本中显示。”因此,这是VC++2015中的一个bug,已经修复,可能会在VC++2017年发布,或者2015年的service pack VC++似乎将
T
推断为
int(int)
,这是
template <typename T> auto f(T &&) -> T;
int i;
int g(int);