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<!std::is_same<T, std::nullptr_t>::value>
而不是 std::enable_if_t<!std::is_same<T, std::nullptr_t>::value>...
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<T>(std::forward<T>(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 (PTVS) 中的 Javascript Intellisense 如何启用?