Определение шаблонных методов с помощью 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>())

Другие вопросы по тегам