VS2013中的SFINAE

时间:2014-05-20 08:34:12

标签: c++ visual-studio c++11

我有一个功能模板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说这是“对重载函数的模糊调用”。

2 个答案:

答案 0 :(得分:5)

我建议在VS2013中避免使用任何非平凡的模板代码。地狱,即使是我称之为微不足道的模板代码也给我带来了麻烦。

对于这种情况,您可以采用部分专门化类模板的旧技术。即使在海湾合作委员会,我也会这样做。如下所示。

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

答案 1 :(得分:0)

我认为问题是由于这个错误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), [](){}));
}