Reputation: 2067
I am trying to create a template speacialization for a function which is based on a type
So for eg for the type number I have
template <class ElementType, typename = typename TEnableIf<TNot<TIsSame<ElementType, double>>::Value>::Type>
void WriteExtras(IGLTFJsonWriter &Writer, const FString &Key, const TSharedPtr<FJsonValue> &Value) const
{
Writer.Write(Key, float(Value->AsNumber()));
// Perform further processing or write logic for double
}
Now I want to have one more definition but for bool ...but if I try do
template <class ElementType, typename = typename TEnableIf<TNot<TIsSame<ElementType, bool>>::Value>::Type>
void WriteExtras(IGLTFJsonWriter &Writer, const FString &Key, const TSharedPtr<FJsonValue> &Value) const
{
Writer.Write(Key, Value->AsBool());
// Perform further processing or write logic for double
}
I get an error Function already defined
I call the function template as
WriteExtras< EJson>(Writer,Key, Pair.Value);
The EJson is an enum
enum class EJson
{
None,
Null,
String,
Number,
Boolean,
Array,
Object
};
After @Jan suggestion following is my template implementation
template <bool Condition, typename T = void> using EnableIf_t = typename TEnableIf<Condition, T>::type;
template <class ElementType>
auto WriteCustom(IGLTFJsonWriter& Writer, const FString& Key, const TSharedPtr<FJsonValue>& Value) const
->EnableIf_t<TNot<TIsSame<ElementType, FString>>::Value>
//->TEnableIf<TIsSame<ElementType, FString>::Value>
{
Writer.Write(Key, Value->AsString());
const FText Title = FText::FromString(TEXT("Template"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("FString")), &Title);
}
template <class ElementType>
auto WriteCustom(IGLTFJsonWriter& Writer, const FString& Key, const TSharedPtr<FJsonValue>& Value) const
->EnableIf_t<TNot<TIsSame<ElementType, bool>>::Value>
//->TEnableIf<TIsSame<ElementType, FString>::Value>
{
Writer.Write(Key, Value->AsBool());
const FText Title = FText::FromString(TEXT("Template"));
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString(TEXT("Bool")), &Title);
}
and when I try to call
WriteCustom<EJson>(Writer,Key, Pair.Value);
it gives error
Upvotes: 1
Views: 145
Reputation: 40259
These two functions actually declare the same function:
template <class ElementType,
class = typename TEnableIf<TNot<TIsSame<ElementType, double>>::Value>::Type>
void WriteExtras(/* same as below */) const;
template <class ElementType,
class = typename TEnableIf<TNot<TIsSame<ElementType, bool>>::Value>::Type>
void WriteExtras(/* same as above */) const;
The only difference between them is the default argument to a template parameter, but that is not making one of these a separate entity. It's like writing:
int foo(int x = 0);
int foo(int x = 1);
enable_if
correctlyYou need to do SFINAE with std::enable_if
(or your custom imitation) somewhere else in the function signature, such as parameters, noexcept specifications, return types, etc. For functions, one of the best approaches is to use the return type, because it doesn't change the template parameter list:
// convenience alias (since C++14)
// similar to std::enable_if_t
template <bool Condition, typename T = void>
using EnableIf_t = typename EnableIf<Condition, T>::type;
// note: you could also make a convenience variable template for TIsSame
template <class ElementType>
auto WriteExtras(/* ... */) const
-> TEnableIf_t<not TIsSame<ElementType, double>::Value>;
// note: TNot is unnecessary, you can just write '!' or 'not' here
template <class ElementType>
auto WriteExtras(/* ... */) const
-> TEnableIf_t<not TIsSame<ElementType, bool>::Value>;
Alternatively, some people prefer to do the SFINAE part in another non-type template parameter (NTTP):
template <class ElementType, TEnableIf_t<not TIsSame<ElementType, double>::Value, int> = 0>
void WriteExtras(/* ... */) const;
The reason why this works is because the functions would have a different type for that NTTP, so the functions aren't actually the same. It also wouldn't be possible to bypass the constraint by providing a template argument explicitly.
However, it's also highly questionable whether you should be using SFINAE here at all. You can achieve the same with if constexpr
(C++17):
template <class ElementType>
void WriteExtras(/* ... */) const {
if constexpr (TIsSame<ElementType, double>::value) {
// ...
}
else if constexpr (TIsSame<ElementType, bool>::value) {
// ...
}
else {
// TODO: handle this error case somehow
// (specifics may depend on language version and compiler)
}
}
You can also use full specializations, as long as there is only one template parameter:
template <class ElementType> // Inside class
// Primary template is defined as deleted, so only calling
// the specializations is allowed.
void WriteExtras(/* ... */) const = delete;
template <> // Outside class
void JsonWriter::WriteExtras<double>(/* ... */) const { /* ... */ }
// More specializations here ...
Upvotes: 2