简体   繁体   English

VS2013中的SFINAE

[英]SFINAE in VS2013

I have a function template forwardMaybeNull . 我有一个函数模板forwardMaybeNull I hope it could forward the first argument if it's not a nullptr and just returns the second argument if the first argument is a nullptr . 我希望它可以转发的第一个参数,如果它不是一个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);
}

It works fine in gcc4.8 but VS2013 says it's a "ambiguous call to overloaded function". 它在gcc4.8中运行良好,但VS2013表示这是“对重载函数的模糊调用”。

I recommend avoiding any non-trivial template code in VS2013. 我建议在VS2013中避免使用任何非平凡的模板代码。 Hell, even template code that I would call trivial has given me trouble. 地狱,即使是我称之为琐碎的模板代码也给了我麻烦。

For this case you can resort to the old technique of partially specialising a class template. 对于这种情况,您可以使用部分特化类模板的旧技术。 I would actually do this even in GCC. 即使在海湾合作委员会,我也会这样做。 Something like the following. 像下面这样的东西。

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

I think the problem is due to this bug https://connect.microsoft.com/VisualStudio/feedback/details/845750/broken-using-template-in-vs2013-update-2-rc . 我认为问题是由于这个错误https://connect.microsoft.com/VisualStudio/feedback/details/845750/broken-using-template-in-vs2013-update-2-rc If you use enable_if rather than enable_if_t it should work. 如果你使用enable_if而不是enable_if_t它应该工作。 Also its usually a good idea to use an unnamed default template parameter rather than a variadic pack for SFINAE in function templates. 另外,在函数模板中使用未命名的默认模板参数而不是SFINAE的可变参数包通常是个好主意。

I have used this pattern quite a lot in MSVC2013 update 2 with no real problems: 我在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);
}

Although the offending alias bug was fixed in MSVC2013 update 3 they essentially broke initializer lists ( http://connect.microsoft.com/VisualStudio/feedbackdetail/view/938122/list-initialization-inside-member-initializer-list-or-non-static-data-member-initializer-is-not-implemented ) so its probably not a good idea to upgrade. 尽管在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 )因此升级可能不是一个好主意。

In this particular case I would suggest using tag dispatch rather than SFINAE to achieve static dispatch: 在这种特殊情况下,我建议使用标签调度而不是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), [](){}));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM