用SFINAE检测模板方法

时间:2018-10-14 17:36:18

标签: c++ c++11 templates sfinae decltype

我有一个简单的特征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 :(得分:1)

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

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在其评论中所述:

  

关键是decltype()返回对象的类型;因此decltype(3)int;当您写decltype(std::true_type)(即decltype(int))时,您会询问类型的类型;您必须询问类型为std::true_type的对象的类型,即decltype(std::true_type{})或(最好是恕我直言)decltype(std::declval<std::true_type>())

答案 1 :(得分:0)

您已经发现,问题在于您必须在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()