Visual C++ 2010,右值引用错误?

Visual C++ 2010, rvalue reference bug?

提问人:Sergey Shandar 提问时间:2/18/2011 最后编辑:Johannes Schaub - litbSergey Shandar 更新时间:10/2/2011 访问量:829

问:

这是Visual C++ 2010中的错误还是正确的行为?

template<class T>
T f(T const &r)
{
    return r;
}

template<class T>
T f(T &&r)
{
    static_assert(false, "no way"); //< line # 10
    return r;
}

int main()
{
    int y = 4;
    f(y); //< line # 17
}

我想,函数 f(T &&) 永远不应该被调用,但它是用 T = int & 调用的。输出:

    main.cpp(10): error C2338: no way
          main.cpp(17) : see reference to function template instantiation 'T f(T)' being compiled
          with
          [
              T=int &
          ]

更新 1你知道任何C++x0编译器作为参考吗?我尝试过 comeau 在线试驾,但无法编译 r 值参考。

更新 2解决方法(使用 SFINAE):

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_reference.hpp>

template<class T>
T f(T &r)
{
    return r;
}

template<class T>
typename ::boost::disable_if< ::boost::is_reference<T>, T>::type f(T &&r)
{
    static_assert(false, "no way");
    return r;
}

int main()
{
    int y = 4;
    f(y);
    // f(5); // generates "no way" error, as expected.
}

更新 3即使没有函数模板实例化,某些编译器也会在 static_assert(false, “no way”) 时触发。解决方法(感谢 @Johannes Schaub - litb)

template<class T> struct false_ { static bool const value = false; };
...
static_assert(false_<T>::value, "no way");

static_assert(sizeof(T) == sizeof(T), "no way");
C++ 11 Visual-C++-2010 右值参考

评论

1赞 UncleBens 2/19/2011
我想知道是否是视觉C++的特殊性,并不总是触发?使用 G++ 时,断言必须依赖于模板参数才能触发,除非实例化函数。static_assert(false, ...)
0赞 Sergey Shandar 2/19/2011
我认为这是一个视觉C++“功能”。对其他编译器使用 'static_assert(sizeof(T) == sizeof(F), “no way”)'。
0赞 GManNickG 2/19/2011
不合规当然不是一个特征。无论如何,是使断言始终失败的好方法,但要依赖。(我有一个模板。sizeof(T) == 0always_false<T>::value
0赞 Sergey Shandar 2/19/2011
@GMan同意了。我在引号里写了“功能”。一种幽默。
1赞 Johannes Schaub - litb 2/26/2011
我添加了一个答案,指出了这一点。既然问题是为什么static_assert会开火,我认为最好有一个静态的答案来解决这个问题。

答:

5赞 Jan Hudec 2/18/2011 #1

据我了解(我可能不完全正确;规范有点复杂),模板类型推导规则对你不利。

编译器首先尝试替换所有模板(此时它还没有选择,只是在寻找选项)并得到:

  • T const &r 将 lvalue 与 匹配,创建 f(int const &) intT = int
  • T &&r 将左值匹配并简化为 ,创建 f(int &)(规范中有这样说的规则)。intT = int&int & &&int&

现在要选择正确的重载,后者更匹配,因为前者在 cv 限定方面不同,而后者则没有。这也是为什么当你删除 时,你会得到模棱两可的重载错误——重载最终会完全相同。const

广告更新1gcc支持许多C++0x功能。您可以从 mingw 获取本机 Windows 构建或使用 cygwin

广告更新2:如果您真的需要对右值和左值进行单独的重载,这似乎是唯一的选择。但是大多数模板对任何类型的引用都做正确的事情,也许用于确保它们调用的函数的正确解析,具体取决于它们是否得到右值或左值)。std::forward

评论

0赞 Sergey Shandar 2/18/2011
谢谢。规范的第 14.8.2.1 节描述了此行为。
0赞 Jean-Simon Brochu 8/20/2014
vector::insert 有同样的错误,它在 VS2013 中修复:stackoverflow.com/questions/25290246/...
3赞 Johannes Schaub - litb #2

但是,您的修复并不能解决触发问题。对于在定义时解析模板的编译器,仍会触发 (大多数) 。static_assertstatic_assert(false, ...)

他们将看到任何函数模板实例化都是格式错误的,并且标准允许他们为模板本身发出错误,并且大多数人都会这样做。

为了完成这项工作,您需要使表达式具有依赖性,以便编译器不知道何时解析模板,它将始终计算为 false。例如

template<class> struct false_ { static bool const value = false; };

template<class T>
T f(T &&r)
{
    static_assert(false_<T>::value, "no way"); //< line # 10
    return r;
}

评论

0赞 Sergey Shandar 2/27/2011
我同意static_assert某些编译器需要此修复程序。谢谢。