英文:
template function override
问题
template <class ElementType, typename = typename TEnableIf<!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
}
template <class ElementType, typename = typename TEnableIf<!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 bool
}
WriteExtras<EJson>(Writer, Key, Pair.Value);
enum class EJson
{
None,
Null,
String,
Number,
Boolean,
Array,
Object
};
英文:
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
答案1
得分: 2
这两个函数实际上声明了相同的函数:
template <class ElementType,
class = typename TEnableIf<TNot<TIsSame<ElementType, double>>::Value>::Type>
void WriteExtras(/* 与下面相同 */) const;
template <class ElementType,
class = typename TEnableIf<TNot<TIsSame<ElementType, bool>>::Value>::Type>
void WriteExtras(/* 与上面相同 */) const;
它们之间唯一的区别是模板参数的默认参数,但这并不使它们成为独立的实体。这就像写:
int foo(int x = 0);
int foo(int x = 1);
## 使用 `enable_if` 正确地进行 SFINAE
你需要在函数签名的其他地方(例如参数、noexcept 说明、返回类型等)使用 `std::enable_if`(或你自定义的模拟版本)。对于函数来说,其中一个最好的方法是在返回类型中使用,因为它不会改变模板参数列表:
```cpp
// 方便的别名(自 C++14 起)
// 类似于 std::enable_if_t
template <bool Condition, typename T = void>
using EnableIf_t = typename EnableIf<Condition, T>::type;
// 注意:你也可以为 TIsSame 做一个方便的变量模板
template <class ElementType>
auto WriteExtras(/* ... */) const
-> TEnableIf_t<!TIsSame<ElementType, double>::Value>;
template <class ElementType>
auto WriteExtras(/* ... */) const
-> TEnableIf_t<!TIsSame<ElementType, bool>::Value>;
或者,一些人更喜欢在另一个非类型模板参数(NTTP)中执行 SFINAE 部分:
template <class ElementType, TEnableIf_t<!TIsSame<ElementType, double>::Value, int> = 0>
void WriteExtras(/* ... */) const;
之所以有效是因为这两个函数对于该 NTTP 具有不同的类型,因此这些函数实际上并不相同。同时,通过显式提供模板参数也无法绕过约束条件。
替代方案
然而,在这里使用 SFINAE 是否合适也是非常值得质疑的。你可以使用 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: 如何处理这种错误情况
// (具体细节可能取决于语言版本和编译器)
}
}
你也可以使用完全特化,只要有一个模板参数:
template <class ElementType> // 类内部
// 主模板被定义为删除,所以只允许调用特化版本。
void WriteExtras(/* ... */) const = delete;
template <> // 类外部
void JsonWriter::WriteExtras<double>(/* ... */) const { /* ... */ }
// 还可以在此添加更多特化版本...
英文:
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);
Doing SFINAE with enable_if
correctly
You 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.
Alternative solutions
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 ...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论