Определение шаблонных методов с помощью SFINAE - PullRequest
0 голосов
/ 14 октября 2018

У меня есть простая черта 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 ]

0 голосов
/ 14 октября 2018

Как вы обнаружили, проблема заключалась в том, что вы должны использовать 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() return, очевидно, std::true_type.

В случаеТип, который является конструируемым по умолчанию, вы можете создать сущность этого типа, просто добавив пару фигурных скобок в конце имени типа. Но когда тип не является конструируемым по умолчанию, вы не можете решить, почему.

С помощью std::declval() вы также получаете выражение заданного типа, когда этот тип не является конструктивным по умолчанию.

В случае std::true_type вы можете решить обоими способами, но я предлагаю использовать всегда std::declval() все равно.

0 голосов
/ 14 октября 2018

Я допустил очень простую ошибку, строка

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>())

...