区分不明确的成员请求错误和 SFINAE 上下文中成员不存在的错误
Differentiate between ambiguous member request error and member does not exist error in SFINAE context?
编辑:发布了我自己的答案,保留了原始接受的答案...让我想到了别名。
编辑:我的问题针对的是区分歧义与在SFINAE(或其他(上下文中成员var/func的存在的可能性。 我的问题不是关于如何制作has_member模板,而是关于检测歧义与存在之间的差异
是否可以设置部分专用化,以区分何时以不明确的方式访问成员(派生类的两个基基都有成员(与成员是否存在(派生类的两个基都没有成员(? 仅当检测到歧义时,我才需要返回 true,但如果根本没有成员,或者它只存在于一个类中,则不需要返回 true。 这就是我到目前为止所拥有的,它为歧义(我想要的(返回 true,对于只有一个具有成员的类(也是我想要的(返回 false,但如果两个类都没有成员,则返回 true(啊!
//for the sake of this example, ClassOne comes from a lib I don't control
struct ClassOne {
//some unknown members in here...
};
struct ClassTwo {
string member_var;
};
template<typename A>
struct helper : std::true_type {};
template<typename A, typename B>
struct merged_class : public A, public B {};
template<typename T, typename = void>
struct has_member_var : std::true_type {
//Member request ambiguous or neither class has member.
//I want to catch these conditions separately,
//but this one template catches both :(
const int status = 1;
};
template<typename T>
struct has_member_var<
T
, typename std::enable_if<
//The next line results in either ambiguous member request error
//if both classes have the member OR
//member does not exist error if neither class has the member
//...how to tell the difference in the type of error?
helper<decltype(T::member_var)>::value
, T
>::type
> : std::false_type {
const int status = 2; //only one class has member
};
//This next one I have no idea how to do, if it's even possible.
//I'd like a third specialization that will match either the
//ambiguous condition or the member only existing in one of the
//base classes.
template<typename T>
struct has_member<
T
, typename std::enable_if<
some_freaky_magic<decltype(T::foo)>::true_value
, T
>::type
> : std::true_type {
const int status = 3;
};
期望用法:
switch(has_member<merged_class<ClassOne, ClassTwo>>::status) {
case 1:
cout << "member ambiguity";
break;
case 2:
cout << "member non-existence";
break;
case 3:
cout << "member existence for only one base";
break;
}
好吧,我想我设法采用了表达式 SFINAE 方法并添加了类型推导。这是一个非常粗略的答案,似乎正在做一些有用的事情(请参阅底部的使用示例(。
(演示文稿可能会更加简洁和干净,但通过这种方式,您可以看到它分解为步骤。
用法:当且仅当A
和B
都有一个名为 x
的成员并且该成员的类型不同(严格来说,不衰减(时,conflicting_X<A, B>::value
为真。中间问题(例如是否为两个类都定义了成员(可以通过 conflicting_X<A, B>::both
和 has_X<T>::value
来决定。
#include <iostream>
#include <type_traits>
#include <typeinfo>
// has_X is taken straight from the other topic
template <typename T>
struct has_X
{
struct Fallback { int x; }; // introduce member name "x"
struct Derived : T, Fallback { };
template<typename C, C> struct ChT;
template<typename C> static char (&f(ChT<int Fallback::*, &C::x>*))[1];
template<typename C> static char (&f(...))[2];
static bool const value = sizeof(f<Derived>(0)) == 2;
};
// Here we go...
template <typename T>
struct XType
{
typedef decltype(T::x) type;
};
template <bool, typename S, typename T>
struct compare_X
{
static const bool value = false;
};
template <typename S, typename T>
struct compare_X<true, S, T>
{
// Note that we don't decay, we want equality on the nose.
static const bool value = ! std::is_same<typename XType<S>::type, typename XType<T>::type>::value;
};
template <typename S, typename T>
struct conflicting_X
{
// We split this up so that XType is only instantiated if T::x really exists.
// You may also use conflicting_X::both as a useful datum.
static const bool both = has_X<S>::value && has_X<T>::value;
static const bool value = compare_X<both, S, T>::value;
};
/*** Example ***/
struct A { int x; };
struct B { int X; };
struct C { double x; };
void f(double) { }
int main() {
std::cout << has_X<A>::value << std::endl; // 1
std::cout << has_X<B>::value << std::endl; // 0
std::cout << "Conflict A/B? " << conflicting_X<A, B>::value << std::endl;
std::cout << "Conflict A/C? " << conflicting_X<A, C>::value << std::endl;
}
更新:我最近对我在原始答案中发布的代码做了更多的事情,所以我正在更新它以考虑更改/添加。
以下是一些使用代码段:*这一切的胆量更远
检查给定类中的成员x
。 可以是 var、func、class、union 或 enum:
CREATE_MEMBER_CHECK(x);
bool has_x = has_member_x<class_to_check_for_x>::value;
检查成员函数void x()
:
//Func signature MUST have T as template variable here... simpler this way :
CREATE_MEMBER_FUNC_SIG_CHECK(x, void (T::*)(), void__x);
bool has_func_sig_void__x = has_member_func_void__x<class_to_check_for_x>::value;
检查成员变量x
:
CREATE_MEMBER_VAR_CHECK(x);
bool has_var_x = has_member_var_x<class_to_check_for_x>::value;
检查成员类x
:
CREATE_MEMBER_CLASS_CHECK(x);
bool has_class_x = has_member_class_x<class_to_check_for_x>::value;
检查会员工会x
:
CREATE_MEMBER_UNION_CHECK(x);
bool has_union_x = has_member_union_x<class_to_check_for_x>::value;
检查成员枚举x
:
CREATE_MEMBER_ENUM_CHECK(x);
bool has_enum_x = has_member_enum_x<class_to_check_for_x>::value;
检查是否有任何成员函数x
而不考虑签名:
CREATE_MEMBER_CHECK(x);
CREATE_MEMBER_VAR_CHECK(x);
CREATE_MEMBER_CLASS_CHECK(x);
CREATE_MEMBER_UNION_CHECK(x);
CREATE_MEMBER_ENUM_CHECK(x);
CREATE_MEMBER_FUNC_CHECK(x);
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
或
CREATE_MEMBER_CHECKS(x); //Just stamps out the same macro calls as above.
bool has_any_func_x = has_member_func_x<class_to_check_for_x>::value;
细节和核心:
/*
- Multiple inheritance forces ambiguity of member names.
- SFINAE is used to make aliases to member names.
- Expression SFINAE is used in just one generic has_member that can accept
any alias we pass it.
*/
template <typename... Args> struct ambiguate : public Args... {};
template<typename A, typename = void>
struct got_type : std::false_type {};
template<typename A>
struct got_type<A> : std::true_type {
typedef A type;
};
template<typename T, T>
struct sig_check : std::true_type {};
template<typename Alias, typename AmbiguitySeed>
struct has_member {
template<typename C> static char ((&f(decltype(&C::value))))[1];
template<typename C> static char ((&f(...)))[2];
//Make sure the member name is consistently spelled the same.
static_assert(
(sizeof(f<AmbiguitySeed>(0)) == 1)
, "Member name specified in AmbiguitySeed is different from member name specified in Alias, or wrong Alias/AmbiguitySeed has been specified."
);
static bool const value = sizeof(f<Alias>(0)) == 2;
};
宏(暗黑破坏神!
CREATE_MEMBER_CHECK:
//Check for any member with given name, whether var, func, class, union, enum.
#define CREATE_MEMBER_CHECK(member)
template<typename T, typename = std::true_type>
struct Alias_##member;
template<typename T>
struct Alias_##member <
T, std::integral_constant<bool, got_type<decltype(&T::member)>::value>
> { static const decltype(&T::member) value; };
struct AmbiguitySeed_##member { char member; };
template<typename T>
struct has_member_##member {
static const bool value
= has_member<
Alias_##member<ambiguate<T, AmbiguitySeed_##member>>
, Alias_##member<AmbiguitySeed_##member>
>::value
;
}
CREATE_MEMBER_VAR_CHECK:
//Check for member variable with given name.
#define CREATE_MEMBER_VAR_CHECK(var_name)
template<typename T, typename = std::true_type>
struct has_member_var_##var_name : std::false_type {};
template<typename T>
struct has_member_var_##var_name<
T
, std::integral_constant<
bool
, !std::is_member_function_pointer<decltype(&T::var_name)>::value
>
> : std::true_type {}
CREATE_MEMBER_FUNC_SIG_CHECK:
//Check for member function with given name AND signature.
#define CREATE_MEMBER_FUNC_SIG_CHECK(func_name, func_sig, templ_postfix)
template<typename T, typename = std::true_type>
struct has_member_func_##templ_postfix : std::false_type {};
template<typename T>
struct has_member_func_##templ_postfix<
T, std::integral_constant<
bool
, sig_check<func_sig, &T::func_name>::value
>
> : std::true_type {}
CREATE_MEMBER_CLASS_CHECK:
//Check for member class with given name.
#define CREATE_MEMBER_CLASS_CHECK(class_name)
template<typename T, typename = std::true_type>
struct has_member_class_##class_name : std::false_type {};
template<typename T>
struct has_member_class_##class_name<
T
, std::integral_constant<
bool
, std::is_class<
typename got_type<typename T::class_name>::type
>::value
>
> : std::true_type {}
CREATE_MEMBER_UNION_CHECK:
//Check for member union with given name.
#define CREATE_MEMBER_UNION_CHECK(union_name)
template<typename T, typename = std::true_type>
struct has_member_union_##union_name : std::false_type {};
template<typename T>
struct has_member_union_##union_name<
T
, std::integral_constant<
bool
, std::is_union<
typename got_type<typename T::union_name>::type
>::value
>
> : std::true_type {}
CREATE_MEMBER_ENUM_CHECK:
//Check for member enum with given name.
#define CREATE_MEMBER_ENUM_CHECK(enum_name)
template<typename T, typename = std::true_type>
struct has_member_enum_##enum_name : std::false_type {};
template<typename T>
struct has_member_enum_##enum_name<
T
, std::integral_constant<
bool
, std::is_enum<
typename got_type<typename T::enum_name>::type
>::value
>
> : std::true_type {}
CREATE_MEMBER_FUNC_CHECK:
//Check for function with given name, any signature.
#define CREATE_MEMBER_FUNC_CHECK(func)
template<typename T>
struct has_member_func_##func {
static const bool value
= has_member_##func<T>::value
&& !has_member_var_##func<T>::value
&& !has_member_class_##func<T>::value
&& !has_member_union_##func<T>::value
&& !has_member_enum_##func<T>::value
;
}
CREATE_MEMBER_CHECKS:
//Create all the checks for one member. Does NOT include func sig checks.
#define CREATE_MEMBER_CHECKS(member)
CREATE_MEMBER_CHECK(member);
CREATE_MEMBER_VAR_CHECK(member);
CREATE_MEMBER_CLASS_CHECK(member);
CREATE_MEMBER_UNION_CHECK(member);
CREATE_MEMBER_ENUM_CHECK(member);
CREATE_MEMBER_FUNC_CHECK(member)
我认为这是不可能的。要使 SFINAE 在此处工作,仅当您要查找的成员不明确时,才需要一个或多个处于特定有效/格式错误的组合中的表达式。这是假设扩展的SFINAE为C++0x。
但是,给定类型 T
和潜在的(非类型(成员i
,有哪些表达式可用? &T::i
,如果将案例限制为仅数据成员,则t.i
t
的类型为 T
的位置。
对于这两个表达式,如果i
不明确,则每个表达式的格式不正确,但如果它存在且不模棱两可,则每个表达式的格式都很好。因此,我们无法满足您的要求。
请注意,给定具有明确数据成员i
的潜在基B
,您可以使用 &T::i
和 t.B::i
进行测试。如果第一个表达式格式不正确,而第二个表达式格式正确,则意味着存在不明确的成员i
。这是我能达到的最接近你的要求。
- 静态数据成员的问题-修复链接错误会导致编译器错误
- C++错误消息*成员参考.**初学者*
- C++错误C2600:无法定义编译器生成的特殊成员函数(必须首先在类中声明)
- 为什么类中的ostringstream类型的成员会导致";调用隐含删除复制构造函数";错误
- 在运算符重载定义中使用成员函数(const错误)
- 错误:请求非类类型为"MULTIMEDIA_FILME [500]"的成员|
- 使用带有 ref 参数的成员函数创建线程时出现编译错误
- 静态成员函数使用相同的名称时出现模板类型名称错误
- 为什么即使我声明了朋友类,我也会收到错误"无法访问类中声明的私人成员"
- 为什么我在空指针错误(链表)中获取成员访问权限
- WinLamb 错误:成员初始化非法
- 类成员值的合法或错误成员用法
- for 循环说 - 错误 成员引用基类型"int [13]"不是结构或联合
- C++错误:成员声明末尾的预期“;”
- C++:错误:成员访问不完整的类型,未使用的参数[-Werror,-Wunused-parameter]
- 错误:成员函数不能在其类之外声明
- 初始化 cpp 文件中的私有静态成员变量.错误:成员是私有的
- 错误:成员访问不完整类型:前向声明
- 错误:成员不可访问
- 指向基类错误成员函数的指针