Определение шаблонных методов с помощью SFINAE
У меня есть простая черта struct hasMemberSerialize
что я пытаюсь использовать, чтобы определить, совместим ли какой-либо данный класс с callSerialize()
, struct
выглядит так:
template<typename Type, typename ArchiveType>
struct hasMemberSerialize {
template<typename T, typename A>
static auto test(int) -> decltype(Serialization::access::callSerialize(std::declval<A&>(), std::declval<T&>()), std::true_type);
template<typename, typename>
static std::false_type test(...);
static const bool value = std::is_same<decltype(test<Type, ArchiveType>(0)), std::true_type>::value;
};
Это компилируется и работает нормально, однако мой hasMemberSerialize::value
всегда std::false_type
, Я использовал аналогичный подход для проверки не шаблонных методов; Тем не менее callSerialize()
Метод, который я проверяю, выглядит примерно так:
template<typename Archive, typename Type>
static auto callSerialize(Archive& a, Type& t) -> decltype(t.serialize(a)) {
// Implementation
}
Я сделал несколько тестов, используя std::cout
вот так:
Serialization::access::callSerialize(JSON, myType);
std::cout << std::boolalpha
<< hasMemberSerialize<MyType, JSONOutputArchive>::value << std::endl;
Вызов метода callSerialize(JSON, myType)
работает как положено и сериализует тип; тем не мение, hasMemberSerialize::value
печать false
, в конце концов, myType
простой тестовый класс:
class MyType {
int myInt;
public:
MyType() : myInt(4) {}
template<typename Archive>
void serialize(Archive& a) {
a(myInt);
}
};
...
MyType myType;
2 ответа
Как вы обнаружили, проблема заключалась в том, что вы должны использовать std::true_type{}
, с концевыми фигурными скобками, в конце decltype()
Так
decltype( <other elements>, std::true_type )
неверно и выдает ошибку, где
decltype( <other elements>, std::true_type{} )
// .......................................^^
работает.
Дело в том, что decltype()
вернуть тип, заданный для сущности (переменная, константа и т. д.) или выражение этого типа; так (на примере) дано decltype(3)
, ты получаешь int
,
Если ты пишешь
decltype( std::true_type )
Вы спрашиваете тип типа, и это неправильно.
Если ты пишешь
decltype( std::true_type{} )
вы спрашиваете тип элемента (std::true_type{}
) типа std::true_type
; это правильно и вы получите std::true_type
,
Я предлагаю другой способ:
decltype( std::declval<std::true_type>() )
где std::declval()
является стандартной функцией шаблона (только объявлена, но достаточно для decltype()
которые возвращают элемент полученного типа шаблона.
Так std::declval<std::true_type>()
это выражение типа std::true_type
а также decltype()
возвращение, очевидно, std::true_type
,
В случае типа, который является конструируемым по умолчанию, вы можете создать объект этого типа, просто добавив пару фигурных скобок в конце имени типа. Но когда тип не является конструируемым по умолчанию, вы не можете решить, почему.
С std::declval()
Вы также получаете выражение данного типа, когда этот тип не является конструируемым по умолчанию.
В случае std::true_type
Вы можете решить в обе стороны, но я предлагаю использовать когда-либо std::declval()
тем не мение.
Я сделал очень простую ошибку, линия
static auto test(int) -> decltype(Serialization::access::callSerialize(std::declval<A&>(), std::declval<T&>()), std::true_type);
должно быть
static auto test(int) -> decltype(Serialization::access::callSerialize(std::declval<A&>(), std::declval<T&>()), std::true_type{});
Обратите внимание: фигурные скобки после std::true_type
,
Как поясняет Макс66 в своем комментарии:
Дело в том, что
decltype()
вернуть тип объекта; такdecltype(3)
являетсяint
; когда ты пишешьdecltype(std::true_type)
(это какdecltype(int)
) вы спрашиваете тип типа; Вы должны спросить тип объекта типаstd::true_type
, то естьdecltype(std::true_type{})
или (лучше, ИМХО)decltype(std::declval<std::true_type>())