考虑代码:
template <typename T>
CByteArray serialize(const T& value)
{
if (std::is_pod<T>::value)
return serializePodType(value);
else if (std::is_convertible<T, Variant>::value)
return serialize(Variant(value));
else
{
assert(0 == "Unsupported type");
return CByteArray();
}
}
显然,编译器对if (std::is_pod<T>::value)
等给我这个警告是正确的,但我该怎样绕过这个呢?我找不到避免这种检查的方法,而且C ++中还没有static if
。
可以使用SFINAE原则来避免这种情况if
吗?
答案 0 :(得分:10)
如果可以使用SFINAE原则来避免这种情况吗?
是的,至少对于非默认情况:
template <typename T>
typename std::enable_if<std::is_pod<T>::value, CByteArray>::type
serialize(const T& value)
{
return serializePodType(value);
}
template <typename T>
typename std::enable_if<
!std::is_pod<T>::value && // needed if POD types can be converted to Variant
std::is_convertible<T, Variant>::value, CByteArray>::type
serialize(const T& value)
{
return serialize(Variant(value));
}
如果您希望不支持类型的运行时而不是编译时错误,则声明一个可变参数函数来捕获任何不匹配其他重载的参数。
CByteArray serialize(...)
{
hlassert_unconditional("Unsupported type");
return CByteArray();
}
答案 1 :(得分:6)
您可以使用以下内容:
template <typename T> CByteArray serialize(const T& value);
namespace detail
{
template <typename T>
CByteArray serializePod(const T& value, std::true_type);
{
return serializePodType(value);
}
template <typename T>
CByteArray serializePod(const T& value, std::false_type);
{
static_assert(std::is_convertible<T, Variant>::value, "unexpect type");
return serialize(Variant(value));
}
}
template <typename T>
CByteArray serialize(const T& value)
{
return detail::serializePod(value, std::is_pod<T>{});
}
答案 2 :(得分:5)
坦率地说,我很想放弃它。编译器证明它知道可以优化掉未使用的分支。当然,警告有点拖累,但是......
无论如何,如果你真的想这样做,请在函数的返回类型上使用std::enable_if
。
答案 3 :(得分:5)
这是怎么回事? http://ideone.com/WgKAju
#include <cassert>
#include <type_traits>
#include <iostream>
class CByteArray { public: CByteArray() {}};
class Variant {};
template<typename T>
CByteArray serializePodType(const T&)
{
printf("serializePodType\n");
return CByteArray();
}
CByteArray serializeVariant(const Variant& v)
{
printf("serializeVariant\n");
return CByteArray();
}
template <typename T>
typename std::enable_if<std::is_pod<T>::value, CByteArray>::type serialize(const T& value)
{
return serializePodType(value);
}
template <typename T>
typename std::enable_if<std::is_convertible<T, Variant>::value && !std::is_pod<T>::value, CByteArray>::type serialize(const T& value)
{
return serializeVariant(Variant(value));
}
class ConvertibleToVariant : public Variant { virtual void foo(); };
struct POD {};
struct NonPOD { virtual void foo(); };
int main()
{
POD pod;
ConvertibleToVariant ctv;
//NonPOD nonpod;
const auto ctv_serialised = serialize(ctv);
const auto pod_serialised = serialize(pod);
//const auto nonpod_serialised = serialize(nonpod);
}
此文档非常适合enable_if
:http://en.cppreference.com/w/cpp/types/enable_if
答案 4 :(得分:2)
现在,您可以使用模板约束来修复它,我喜欢使用一个小宏来帮助使enable_if
样板更清晰:
#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0
然后你可以直接在函数中定义它们:
template <typename T, REQUIRES(std::is_pod<T>::value)>
CByteArray serialize(const T& value)
{
return serializePodType(value);
}
template <typename T, REQUIRES(
!std::is_pod<T>::value &&
!std::is_convertible<T, Variant>::value
)>
CByteArray serialize(const T& value)
{
assert(0 == "Unsupported type");
return CByteArray();
}
// This is put last so `serialize` will call the other overloads
template <typename T, REQUIRES(
!std::is_pod<T>::value &&
std::is_convertible<T, Variant>::value
)>
CByteArray serialize(const T& value)
{
return serialize(Variant(value));
}
然而,这很快变得丑陋。首先,你必须否定其他条件以避免歧义。其次,必须对函数进行排序,以便在递归调用其他函数之前声明或定义它们。它并没有很好地扩展。如果您将来需要添加其他条件,可能会变得更加复杂。
更好的解决方案是将conditional overloading与fix point combinator一起使用。 Fit库提供了conditional和fix适配器,因此您无需编写自己的适配器。所以在C ++ 14中,你可以写:
const constexpr serialize = fit::fix(fit::conditional(
FIT_STATIC_LAMBDA(auto, const auto& value,
REQUIRES(std::is_pod<decltype(value)>()))
{
return serializePodType(value);
},
FIT_STATIC_LAMBDA(auto self, const auto& value,
REQUIRES(std::is_convertible<decltype(value), Variant>()))
{
return self(Variant(value));
},
FIT_STATIC_LAMBDA(auto, const auto&)
{
assert(0 == "Unsupported type");
return CByteArray();
}
));
但是,如果您还没有使用C ++ 14,则必须将它们写为函数对象:
struct serialize_pod
{
template<class Self, class T,
REQUIRES(std::is_pod<T>::value)>
CByteArray operator()(Self, const T& value) const
{
return serializePodType(value);
}
};
struct serialize_variant
{
template<class Self, class T,
REQUIRES(std::is_convertible<T, Variant>::value)>
CByteArray operator()(Self self, const T& value) const
{
return self(Variant(value));
}
};
struct serialize_else
{
template<class Self, class T>
CByteArray operator()(Self, const T&) const
{
assert(0 == "Unsupported type");
return CByteArray();
}
};
const constexpr fit::conditional_adaptor<serialize_pod, serialize_variant, serialize_else> serialize = {};
最后,对于您的具体情况,您可以删除else部分,除非您确实需要运行时检查。然后你就可以有两个重载:
const constexpr serialize = fit::fix(fit::conditional(
FIT_STATIC_LAMBDA(auto, const auto& value,
REQUIRES(std::is_pod<decltype(value)>()))
{
return serializePodType(value);
},
FIT_STATIC_LAMBDA(auto self, const auto& value,
REQUIRES(std::is_convertible<decltype(value), Variant>()))
{
return self(Variant(value));
}
));
因此,您将遇到编译器错误。使用enable_if
和约束的好处是错误将出现在用户代码中而不是代码中(带有一些长回溯)。这有助于明确用户是出错的人而不是库代码的问题。