[英]How to avoid “conditional expression is constant” warning with compile-time-constant conditions in template code?
考虑一下代码:
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)
等给我这个警告是正确的,但是我该怎样绕过这个呢? 我找不到避免这种检查的方法, static if
在C ++中还没有static if
。
if
可以使用SFINAE原则来避免这种if
吗?
如果可以使用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();
}
您可以使用以下内容:
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>{});
}
坦率地说,我很想放弃它。 编译器证明它知道可以优化掉未使用的分支。 当然,警告有点拖累,但..
无论如何,如果你真的想这样做,请在函数的返回类型上使用std::enable_if
。
#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
现在,您可以使用模板约束来修复它,我喜欢使用一个小宏来帮助使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));
}
然而,这很快变得丑陋。 首先,你必须否定其他条件以避免歧义。 其次,必须对函数进行排序,以便在递归调用其他函数之前声明或定义它们。 它并没有很好地扩展。 如果您将来需要添加其他条件,可能会变得更加复杂。
更好的解决方案是使用带有固定点组合器的 条件重载 。 Fit库提供了条件和修复适配器,因此您不必编写自己的适配器。 所以在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
和约束的enable_if
是错误将出现在用户代码中而不是代码中(有一些长回溯)。 这有助于清楚地表明用户是出错的人而不是库代码的问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.