C++ 2013年VSINAE
我有一个函数模板C++ 2013年VSINAE,c++,visual-studio,c++11,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,
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);
}
模板
U&&forwardMaybeNull(标准::nullptr\U t,U&&rvalue\U默认值){
返回std::move(右值默认值);
}
模板…>
T&&forwardMaybeNull(标准::删除\U参考\U T&arg,U&&){
返回静态_cast(arg);
}
模板…>
T&&forwardMaybeNull(标准::删除\U引用\U T&&arg,U&&){
返回静态_cast(arg);
}
模板
空条(T&&{}
模板
无效Foo(T&T){
Bar(前向maybenull(t,[](){}));
}
int main(){
Foo(nullptr);
}
它在gcc4.8中运行良好,但VS2013说它是“对重载函数的不明确调用”。我建议避免在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));
}
名称空间详细信息{
模板
结构转发程序{
使用结果_type=T&;
静态T&&call(std::remove_reference_T&arg,U&&){
返回静态_cast(arg);
}
静态T&&call(std::remove_reference_T&&arg,U&&){
返回静态_cast(arg);
}
};
模板
结构转发程序{
使用结果_type=U&;
静态U&&call(std::nullptr\U t、U&&rvalue\U默认值){
返回std::move(右值默认值);
}
};
}
模板
typename详细信息::forwarderMaybeNull::结果\类型forwardMaybeNull(
标准::删除\U引用\U t&arg、U&U){
返回详细信息::ForwarderMyBenull::调用(std::forward(arg),
标准:正向(u));
}
模板
typename详细信息::forwarderMaybeNull::结果\类型forwardMaybeNull(
标准::删除\U引用\U t&&arg,U&&U){
返回详细信息::ForwarderMyBenull::调用(std::forward(arg),
标准:正向(u));
}
我认为问题是由于这个错误造成的。如果使用enable_If而不是enable_If,它应该可以工作。对于函数模板中的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);
}
模板::类型>
U&&forwardMaybeNull(标准::nullptr\U t,U&&rvalue\U默认值){
返回std::move(右值默认值);
}
尽管在MSVC2013更新3中修复了有问题的别名错误,但它们基本上破坏了初始值设定项列表(),因此升级可能不是一个好主意
在这种情况下,我建议使用标记分派而不是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), [](){}));
}
模板
U&&forwardMaybeNull(标准::true_类型,T,U&&rvalue_默认值){
返回std::move(右值默认值);
}
模板
T&&forwardMaybeNull(标准::false_类型,T&&arg,U&&){
返回标准::转发(arg);
}
模板。
无效Foo(T&T){
Bar(forwardMaybeNull(typename std::is_same::type{},std::forward(t),[](){});
}
我的建议是在VS2013中尽可能避免使用SFINAE,或者最好尽可能避免使用VS2013。这是一个痛苦的世界。已经超过了SFINAE的标准。可变无效包真的是获得SFINAE的合法方法吗?1)你的第三个转发包可能是第二个转发包的精确副本。2) 使用typename=std::enable_if_t::value>
而不是std::enable_if_t::value>。
3)利润(VS2013更新2)4)注意enable_if_t和SFINAE()新引入的错误谢谢您的评论。1) 第二个forwardMaybeNull
采用左值引用,而第三个采用右值引用。2) @QingYun 1)哦,我没有注意到,对不起2)我在Scott Meyers的博客中看到了这篇文章的链接,但遗憾的是,它在VSIt中无法正常工作。对于左值引用和右值引用,似乎仍然需要重载,就像std::forward
一样。或者显式地std::forward
forwardMaybeNull的参数,比如forwardMaybeNull(std::forward(t),[](){})
.Hmm,这是真的。我应该把它修好。