使用 SFINAE 检测模板方法

Dectecting template methods with SFINAE

我有一个简单的特性 struct hasMemberSerialize,我试图用它来确定任何给定的 class 是否与 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::valuealways 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:

class MyType {
    int myInt;

public:
    MyType() : myInt(4) {}

    template<typename Archive>
    void serialize(Archive& a) {
        a(myInt);
    }
};

...

MyType myType;

我犯了一个很简单的错误,行

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后的花括号.

正如 Max66 在他的评论中解释的那样:

the point is that decltype() return the type of an object; so decltype(3) is int; when you write decltype(std::true_type) (that is as decltype(int)) you ask the type of a type; you have to ask the type of an object of type std::true_type, that is decltype(std::true_type{}) or (better, IMHO) decltype(std::declval<std::true_type>())

正如您所发现的,问题在于您必须在 decltype()

的末尾使用 std::true_type{},并使用结束大括号

所以

decltype( <other elements>, std::true_type )

错了,报错,其中

decltype( <other elements>, std::true_type{} )
// .......................................^^

有效。

重点是decltype() return给定实体(变量、常量等)的类型或该类型的表达式;所以(通过例子)给定 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() return 接收到模板类型的元素。

所以std::declval<std::true_type>()std::true_typedecltype()return类型的表达式,显然,std::true_type.

对于默认可构造的类型,您可以创建该类型的实体,只需在类型名称的末尾添加几个大括号即可。但是当一个类型不是默认可构造的时,你无法解决这个原因。

使用 std::declval() 时,当该类型不是默认可构造的时,您也会得到给定类型的表达式。

std::true_type 的情况下,你可以用两种方式解决,但我建议无论如何都要使用 std::declval()