[英]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.