VS2013 中的 SFINAE

Posted

技术标签:

【中文标题】VS2013 中的 SFINAE【英文标题】:SFINAE in VS2013 【发布时间】:2014-05-20 08:34:12 【问题描述】:

我有一个函数模板forwardMaybeNull。如果不是nullptr,我希望它可以转发第一个参数,如果第一个参数是nullptr,则只返回第二个参数。

template <typename T, typename U,
          std::enable_if_t<std::is_same<T, std::nullptr_t>::value>...>
U&& forwardMaybeNull(std::nullptr_t, U&& rvalue_default) 
  return std::move(rvalue_default);


template <typename T, typename U,
          std::enable_if_t<!std::is_same<T, std::nullptr_t>::value>...>
T&& forwardMaybeNull(std::remove_reference_t<T>& arg, U&&) 
  return static_cast<T&&>(arg);


template <typename T, typename U,
          std::enable_if_t<!std::is_same<T, std::nullptr_t>::value>...>
T&& forwardMaybeNull(std::remove_reference_t<T>&& arg, U&&) 
  return static_cast<T&&>(arg);


template <typename T>
void Bar(T&&) 

template <typename T>
void Foo(T&& t) 
    Bar(forwardMaybeNull<T>(t, []()));


int main() 
  Foo(nullptr);

它在 gcc4.8 中运行良好,但 VS2013 说这是“对重载函数的模糊调用”。

【问题讨论】:

我的建议是在 VS2013 中尽可能避免使用 SFINAE,或者更好的是,尽可能避免使用 VS2013。这是一个痛苦的世界。已经超过了 SFINAE。 可变参数 void 包真的是获得 SFINAE 的合法方式吗? 1) 您的第三个 forwardMaybeNull 是第二个的精确副本。 2) 使用 typename = std::enable_if_t&lt;!std::is_same&lt;T, std::nullptr_t&gt;::value&gt; 而不是 std::enable_if_t&lt;!std::is_same&lt;T, std::nullptr_t&gt;::value&gt;... 3) 利润 (VS2013 更新 2) 4) 注意新引入的错误 enable_if_t 和 SFINAE (connect.microsoft.com/VisualStudio/feedback/details/872124/…) @user2665887 感谢您的评论。 1) 第二个forwardMaybeNull 采用左值引用,而第三个采用右值引用。 2)flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html @QingYun 1) 哦,我没注意到,抱歉 2) 我在 Scott Meyers 的博客中看到了该文章的链接,但遗憾的是它在 VS 中无法正常工作跨度> 【参考方案1】:

我建议避免在 VS2013 中使用任何重要的模板代码。该死,即使是我认为微不足道的模板代码也给我带来了麻烦。

对于这种情况,您可以求助于部分专门化类模板的旧技术。实际上,即使在 GCC 中,我也会这样做。类似于以下内容。

namespace detail 

template <typename T, typename U>
struct forwarderMaybeNull 
  using result_type = T&&;
  static T&& call(std::remove_reference_t<T>& arg, U&&) 
    return static_cast<T&&>(arg);
  

  static T&& call(std::remove_reference_t<T>&& arg, U&&) 
    return static_cast<T&&>(arg);
  
;

template <typename U>
struct forwarderMaybeNull<nullptr_t, U> 
  using result_type = U&&;
  static U&& call(std::nullptr_t, U&& rvalue_default) 
    return std::move(rvalue_default);
  
;



template <typename T, typename U>
typename detail::forwarderMaybeNull<T, U>::result_type forwardMaybeNull(
    std::remove_reference_t<T>& arg, U&& u) 
  return detail::forwarderMaybeNull<T, U>::call(std::forward<T>(arg),
                                                std::forward<U>(u));

template <typename T, typename U>
typename detail::forwarderMaybeNull<T, U>::result_type forwardMaybeNull(
    std::remove_reference_t<T>&& arg, U&& u) 
  return detail::forwarderMaybeNull<T, U>::call(std::forward<T>(arg),
                                                std::forward<U>(u));

【讨论】:

左值引用和右值引用似乎仍然需要重载,就像std::forward 一样。或者明确地将std::forward 参数传递给forwardMaybeNull,例如forwardMaybeNull&lt;T&gt;(std::forward&lt;T&gt;(t), []()) 嗯,确实如此。我应该修复它。【参考方案2】:

我认为问题是由于这个错误https://connect.microsoft.com/VisualStudio/feedback/details/845750/broken-using-template-in-vs2013-update-2-rc。如果您使用 enable_if 而不是 enable_if_t 它应该可以工作。此外,在函数模板中使用未命名的默认模板参数而不是 SFINAE 的可变参数包通常是一个好主意。

我在 MSVC2013 更新 2 中经常使用这种模式,没有任何实际问题:

template <typename T, typename U, typename = typename std::enable_if<std::is_same<
    T, std::nullptr_t>::value>::type>
U&& forwardMaybeNull(std::nullptr_t, U&& rvalue_default) 
  return std::move(rvalue_default);

虽然在 MSVC2013 更新 3 中修复了令人讨厌的别名错误,但它们基本上破坏了初始化列表 (http://connect.microsoft.com/VisualStudio/feedbackdetail/view/938122/list-initialization-inside-member-initializer-list-or-non-static-data-member-initializer-is-not-implemented),因此升级可能不是一个好主意。

在这种特殊情况下,我建议使用标签调度而不是 SFINAE 来实现静态调度:

template <typename T, typename U>
U&& forwardMaybeNull(std::true_type, T, U&& rvalue_default) 
  return std::move(rvalue_default);


template <typename T, typename U>
T&& forwardMaybeNull(std::false_type,T&& arg, U&&) 
  return std::forward<T>(arg);


template <typename T>. 
void Foo(T&& t) 
    Bar(forwardMaybeNull(typename std::is_same<T, std::nullptr_t>::type, std::forward<T>(t), []()));

【讨论】:

以上是关于VS2013 中的 SFINAE的主要内容,如果未能解决你的问题,请参考以下文章

Intellisense 不适用于 VS2013 中的 JS

vs2013 中的数据库模式查看器功能

VS2013 (PTVS) 中的 Javascript Intellisense 如何启用?

VS2013 中的 SFINAE

如何修复从 VS2010 升级到 VS2013 的项目中的链接器错误,其中链接器正在寻找不存在的 MFC 库文件?

VLFeat开源库介绍及在VS2013中的编译