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()
givevoid(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
};