我已经看到这个question允许人们检查是否存在成员函数,但我试图找出一个类是否有成员类型
在下面的示例中,两者都评估为“false”,但我想找到一种方法,以便has_bar<foo1>::value
评估为false,has_bar<foo2>::value
评估为 true
这可能吗?
#include <iostream>
struct foo1;
struct foo2 { typedef int bar; };
template <typename T>
class has_bar
{
typedef char yes;
typedef long no;
template <typename C> static yes check( decltype(&C::bar) ) ;
template <typename C> static no check(...);
public:
enum { value = sizeof(check<T>(0)) == sizeof(yes) };
};
int main()
{
std::cout << has_bar<foo1>::value << std::endl;
std::cout << has_bar<foo2>::value << std::endl;
return 0;
}
编辑:针对以下答案实施专业化:
...如果您在目标模板中使用C :: bar,则模板将是 对于没有嵌套类型的类型自动丢弃。
我试过这样做,但显然遗漏了一些东西
#include <iostream>
struct foo1;
struct foo2 { typedef int bar; };
template <typename T, typename U = void>
struct target
{
target()
{
std::cout << "default target" << std::endl;
}
};
template<typename T>
struct target<T, typename T::bar>
{
target()
{
std::cout << "specialized target" << std::endl;
}
};
int main()
{
target<foo1>();
target<foo2>();
return 0;
}
答案 0 :(得分:14)
试试这个
template<class T>
struct Void {
typedef void type;
};
template<class T, class U = void>
struct has_bar {
enum { value = 0 };
};
template<class T>
struct has_bar<T, typename Void<typename T::bar>::type > {
enum { value = 1 };
};
答案 1 :(得分:13)
您无法获取指向类型成员的成员的指针:
template <typename C> static yes check( decltype(&C::bar) ) ;
仅当&C::bar
是bar
的非类型成员时,子表达式C
才有效。但您需要检查的是它是否是类型。对模板的最小更改可能是:
template <typename C> static yes check( typename C::bar* ) ;
如果bar
是C
的嵌套类型,那么该函数重载将是一个有效的候选者(0将是指向任何C::bar
类型的指针),但如果{ {1}}不包含嵌套的C
,然后它将被丢弃,第二次测试将是唯一的候选者。
关于是否需要特征存在一个不同的问题,因为如果在目标模板中使用bar
,则对于没有嵌套类型的类型,模板将自动被丢弃。 / p>
修改的
我的意思是,在您的方法中,您需要为每个可能的嵌套类型创建特征,只是为了生成一个包含或不包含嵌套类型的模板(C::bar
)。让我们采取不同的方法......首先,我们定义一个通用实用程序来根据条件选择一个类型,对于这个问题,这不是 required ,而更简单的enable_if
就足够了,但是实用程序模板在其他情况下很有用:
template <typename T> void_type { typedef void type; };
现在只需要使用SFINAE进行类模板特化:
// General utility: if_<Condition, Then, Else>::type
// Selects 'Then' or 'Else' type based on the value of
// the 'Condition'
template <bool Condition, typename Then, typename Else = void>
struct if_ {
typedef Then type;
};
template <typename Then, typename Else>
struct if_<false, Then, Else > {
typedef Else type;
};
请注意,与您的方法的主要区别在于使用额外的中间模板(替换将失败并且不是错误的模板)产生template <typename T, typename _ = void>
struct target {
// generic implementation
};
template <typename T>
struct target<T, typename if_<false,typename T::bar>::type> {
// specialization for types holding a nested type `T::bar`
};
类型(成功时)。这就是为什么上面的void
模板也可以工作的原因:你只需要使用嵌套类型作为模板的参数,并且让它失败,你不关心模板的作用,只要如果成功,评估是嵌套的void_type
(必须是type
)。
如果不明显(我最初并不是这样)为什么你的方法不起作用,请考虑编译器在遇到void
时需要做什么:第一步是找到那里是一个名为target<foo2>
的模板,但该模板有两个参数,其中只提供了一个参数。然后它在基本模板(非专用的模板)中查找并发现第二个参数可以默认为target
。从这一点开始,它会将您的实例化视为:void
(在注入默认参数之后)。它会尝试匹配最好的专业化。仅考虑第二个参数 target<foo2,void>
的特化。如果void
为T::bar
,您上面的模板将只能使用专用版本(您可以通过将void
更改为:foo2
进行测试。因为您不想要当嵌套类型为struct foo2 { typedef void bar; }
时,需要使用<{1}}的额外模板来启用 的专门化(如果类型不包含嵌套的{{}则会失败1}})但总是将void
作为嵌套类型。
答案 2 :(得分:2)
我更喜欢用宏包装它。
test.h:
#include <type_traits>
template<typename ...>
struct void_type
{
using type = void;
};
template<typename ...T>
using void_t = typename void_type<T...>::type;
#define HAS_TYPE(NAME) \
template<typename, typename = void> \
struct has_type_##NAME: std::false_type \
{}; \
template<typename T> \
struct has_type_##NAME<T, void_t<typename T::NAME>>: std::true_type \
{} \
HAS_TYPE(bar);
TEST.CPP:
#include <iostream>
struct foo1;
struct foo2 { typedef int bar; };
int main()
{
std::cout << has_type_bar<foo1>::value << std::endl;
std::cout << has_type_bar<foo2>::value << std::endl;
return 0;
}
答案 3 :(得分:1)
C++20 更新:
现在检查给定类型是否包含特定类型定义变得更加容易。
template<typename T>
concept has_bar = requires {
typename T::bar;
};
...所以您的示例代码演变为:
#include <iostream>
struct foo1;
struct foo2 { typedef int bar; };
template <typename T, typename U = void>
struct target
{
target()
{
std::cout << "default target" << std::endl;
}
};
template<typename T>
requires(has_bar<T>)
struct target<T>
{
target()
{
std::cout << "specialized target" << std::endl;
}
};
int main()
{
target<foo1>();
target<foo2>();
return 0;
}
gcc.godbolt 上的示例:https://gcc.godbolt.org/z/a15G13