C++ 匹配继承成员函数的类型

C++ 匹配继承成员函数的类型,c++,c++11,match,sfinae,member-pointers,C++,C++11,Match,Sfinae,Member Pointers,我有下面的代码片段,没有编译 #include <iostream> struct A { void foo() {} }; struct B : public A { using A::foo; }; template<typename U, U> struct helper{}; int main() { helper<void (A::*)(), &A::foo> compiles; helper<v

我有下面的代码片段,没有编译

#include <iostream>

struct A {
    void foo() {}
};

struct B : public A {
    using A::foo;
};

template<typename U, U> struct helper{};

int main() {
    helper<void (A::*)(), &A::foo> compiles;
    helper<void (B::*)(), &B::foo> does_not_compile;

    return 0;
}
#包括
结构A{
void foo(){}
};
结构B:公共A{
使用A::foo;
};
模板结构助手{};
int main(){
助手编译;
助手不编译;
返回0;
}
它不会编译,因为
&B::foo
解析为
&A::foo
,因此它无法匹配建议的类型
void(B::*)()
。由于这是SFINAE模板的一部分,我使用它来检查非常特定的接口(我强制特定的参数类型和输出类型),因此我希望它独立于继承工作,同时保持检查的可读性

我尝试的内容包括:

  • 铸造论点的第二部分:

    helper不编译

    不幸的是,这没有帮助,因为第二部分现在没有被识别为常量表达式,并且失败了

  • 我尝试将引用分配给一个变量,以便检查它

    consteprvoid(B::*p)(=&B::foo;
    helper半自动编译

    这段代码被Clang3.4接受,但g++4.8.1拒绝了它,我不知道谁是对的

有什么想法吗

编辑:由于许多评论要求更具体的问题版本,我将在这里写下:

我要寻找的是一种显式检查类是否尊重特定接口的方法。此检查将用于验证模板化函数中的输入参数,以便它们遵守这些函数所需的约定,以便在类和函数不兼容的情况下提前停止编译(即类型特征检查)


因此,我需要能够验证我请求的每个成员函数的返回类型、参数类型和数量、常量等。最初的问题是我用来验证匹配的更大模板的检查部分。

如果您只是想检查给定类型T上是否存在接口,那么有更好的方法。以下是一个例子:

template<typename T>
struct has_foo
{
    template<typename U>
    constexpr static auto sfinae(U *obj) -> decltype(obj->foo(), bool()) { return true; }

    constexpr static auto sfinae(...) -> bool { return false; }

    constexpr static bool value = sfinae(static_cast<T*>(0));
};

希望对您有所帮助。

下面给出了您的问题的有效解决方案,请参阅

从某种意义上说,这是一个后续问题。在中,我定义了一个类型trait
member\u class
,它从指向成员函数类型的给定指针中提取一个类。下面我们用更多的特征来分析,然后合成出这样一种类型

首先,
member\u type
提取签名,例如
void(C::*)()
给出
void()

最后,
member\u ptr
合成指向给定类和签名的成员函数类型的指针,例如
C
+
void()
give
void(C:*)()

template <typename C, typename S>
struct member_ptr_t;

template <typename C, typename S>
using member_ptr = typename member_ptr_t <C, S>::type;

template <typename C, typename R, typename ...A>
struct member_ptr_t <C, R(A...)> { using type = R (C::*)(A...); };

template <typename C, typename R, typename ...A>
struct member_ptr_t <C const, R(A...)> { using type = R (C::*)(A...) const; };

// ...other qualifier specializations
给定类型
Z
,别名模板
pattern
使用
decltype(&Z::foo)
)获取成员指针的正确类型
M
,提取其
衰减
的类
C
和签名
S
,并使用类
C const
和签名
void()
合成指向成员函数类型的新指针,即
void(C::*)()const
。这正是您所需要的:它与原始硬编码模式相同,类型
Z
替换为正确的类
C
(可能是基类),如
decltype
所示

以图形方式:

M = void (Z::*)() const  ->   Z         +   void()
                         ->   Z const   +   void()
                         ->   void (Z::*)() const   ==   M
                         ->   SUCCESS

M = int (Z::*)() const&  ->   Z const&   +   int()
                         ->   Z const    +   void()
                         ->   void (Z::*)() const   !=   M
                         ->   FAILURE
事实上,这里不需要签名
S
,因此
成员类型也不需要签名。但我在这个过程中使用了它,所以为了完整起见,我将它包括在这里。它在更一般的情况下可能有用

当然,所有这些对于多个重载都不起作用,因为
decltype
在这种情况下不起作用。

我建议:

helper编译;
助手也会编译;

这似乎是一种违规行为,但从根本上讲,重复名称并不比单独指定类型和名称更糟糕。

这里有一个简单的类,它通过了您的测试(并且不需要一打专门化:))。当
foo
过载时,它也可以工作。您希望检查的签名也可以是模板参数(这是件好事,对吧?)

现在
is\u foo::value
将给出
false
,因为
foo
的名称查找将在遇到
Y::foo
时停止。如果这不是您想要的行为,考虑将您希望执行查找的类作为“代码> iSfFoo的模板参数,并使用它代替<代码>和U::Foo


希望能有所帮助。

您能提供更多关于您努力实现目标的详细信息吗?可能有另一种方法可以做到这一点。正如前面提到的
decltype
,我建议您可以编写
helper编译?最终目标如下:(如果你认为值得,我可以在这里复制它,我认为它有损你的利益)。正如我所说的,我们的想法是尽可能精确地验证公共接口,同时保持可读性。@IvanVergiliev问题是,无论foo的实际签名是什么,它都将匹配foo,而我想验证它是否匹配我需要的。@Svalorzen:你的目标是什么?如果您只是想检查给定类型的
T
上是否存在接口,那么有更好的方法来检查。问题是,它允许几乎任何类型的
foo
函数。它不检查constness和virtual,它允许任何返回类型,并允许在参数中进行转换(int到unsigned和类似)。这就是为什么我试图避免它。@Svalorzen:好的。然后看这个:。。。这对你合适吗?当然,它只检查参数类型,不检查返回类型(这很容易做到)。它为
C
D
类类型(其
foo
需要转换)打印
0
(false)!这是一个非常巧妙的把戏!你能让它也检查一下constn吗
template <typename M> struct member_type_t { };
template <typename M> using  member_type = typename member_type_t <M>::type;

template <typename T, typename C>
struct member_type_t <T C::*> { using type = T;};
template<typename>
struct member_class_t;

template<typename M>
using member_class = typename member_class_t <M>::type;

template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...)> { using type = C; };

template<typename R, typename C, typename... A>
struct member_class_t <R(C::*)(A...) const> { using type = C const; };

// ...other qualifier specializations
template <typename C, typename S>
struct member_ptr_t;

template <typename C, typename S>
using member_ptr = typename member_ptr_t <C, S>::type;

template <typename C, typename R, typename ...A>
struct member_ptr_t <C, R(A...)> { using type = R (C::*)(A...); };

template <typename C, typename R, typename ...A>
struct member_ptr_t <C const, R(A...)> { using type = R (C::*)(A...) const; };

// ...other qualifier specializations
template <typename T>
struct is_foo {
private:
    template<
        typename Z,
        typename M = decltype(&Z::foo),
        typename C = typename std::decay<member_class<M>>::type,
        typename S = member_type<M>
    >
    using pattern = member_ptr<C const, void()>;

    template<typename U, U> struct helper{};

    template <typename Z> static auto test(Z z) -> decltype(
        helper<pattern<Z>, &Z::foo>(),
        // All other requirements follow..
        std::true_type()
    );

    template <typename> static auto test(...) -> std::false_type;

public:
    enum { value = std::is_same<decltype(test<T>(std::declval<T>())),std::true_type>::value };
};
M = void (Z::*)() const  ->   Z         +   void()
                         ->   Z const   +   void()
                         ->   void (Z::*)() const   ==   M
                         ->   SUCCESS

M = int (Z::*)() const&  ->   Z const&   +   int()
                         ->   Z const    +   void()
                         ->   void (Z::*)() const   !=   M
                         ->   FAILURE
helper<decltype(&A::foo), &A::foo> compiles;
helper<decltype(&B::foo), &B::foo> also_compiles;
#include <type_traits>

template <typename T>
struct is_foo {
    template<typename U>
    static auto check(int) ->
    decltype( static_cast< void (U::*)() const >(&U::foo), std::true_type() );
    //                     ^^^^^^^^^^^^^^^^^^^
    //                     the desired signature goes here

    template<typename>
    static std::false_type check(...);

    static constexpr bool value = decltype(check<T>(0))::value;
};
struct X {
    void foo() const;
};

struct Y : X {
    int foo();   // hides X::foo
};