可以使用type_traits / SFINAE来查找类是否定义了成员TYPE? [英] Possible to use type_traits / SFINAE to find if a class defines a member TYPE?

查看:205
本文介绍了可以使用type_traits / SFINAE来查找类是否定义了成员TYPE?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我看到这个问题,它允许检查成员函数的存在,但我想知道类是否具有成员类型



在下面的例子中,两个都计算为false,但我想找到一种方法,使 has_bar< foo1> / code>计算为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>静态无检查(...)
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;
}

编辑:实现专业化以回应以下答案: / strong>


...如果在目标模板中使用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< 专用目标< std :: endl;
}
};

int main()
{
target< foo1>();
target< foo2>();
return 0;
}


解决方案

成员到类型成员:

 模板< typename C> static yes check(decltype(& C :: bar)); 

子表达式& C :: bar 将仅在 bar C 的非类型成员时有效。但您需要检查的是是否是类型。对模板的最小更改可以是:

 模板< typename C& static是检查(typename C :: bar *); 

如果 bar 是嵌套类型 C ,那么函数重载将是一个有效的候选项(0将是一个指向 C :: bar 类型的指针is),但如果 C 不包含嵌套 bar ,那么它将被丢弃,第二个测试将是只有候选人。



有一个不同的问题,是否需要trait,因为如果你使用 C :: bar




p> EDIT



我的意思是在你的方法中,你需要为每个可能的嵌套类型创建一个trait,一个包含或不包含嵌套类型( enable_if )的模板。让我们采用不同的方法...首先,我们定义一个通用实用程序来基于条件选择一个类型,这不是对这个问题的要求,而是一个更简单的 template<类型名T> void_type {typedef void type; }; 就足够了,但是实用程序模板在其他情况下很有用:

 实用程序:if_< Condition,Then,Else> :: type 
//基于
的值选择'Then'或'Else'类型//'Condition'
template< bool Condition,typename然后,typename Else = void>
struct if_ {
typedef然后键入;
};
template< typename然后,typename Else>
struct if_< false,Then,Else> {
typedef其他类型;
};

现在只需要使用SFINAE类模板专门化:

  template< typename T,typename _ = void> 
struct target {
//通用实现
};

template< typename T>
struct target< T,typename if_< false,typename T :: bar> :: type> {
//特殊化的类型为嵌套类型`T :: bar`
};

请注意,与您的方法的主要区别是使用一个额外的中间模板替换将失败 - 并且不是错误),产生 void 类型(成功时)。这就是为什么上面的 void_type 模板也可以工作的原因:你只需要使用嵌套类型作为模板的参数,并且失败,你不真的只要评估是嵌套的类型(必须 void ),如果成功。



如果它不明显(这不是我的初衷)为什么你的方法不工作,考虑编译器遇到时需要做什么 target< foo2> :第一步是发现有一个名为 target 的模板,其中只有一个提供。然后它查找基本模板(非专门的模板),发现第二个参数可以默认为 void 。从这一点开始,它将考虑你的实例化是: target< foo2,void> (注入默认参数后)。它会尝试匹配最好的专业化。仅考虑第二个参数 void 的专业化。如果 T :: bar void ,您的模板将只能使用专用版本通过将 foo2 更改为: struct foo2 {typedef void bar;} c c $ c>(如果类型不包含嵌套的 bar ,则会失败),但总会 yield code>作为嵌套类型。


I have seen this question which allows one to check for the existence of a member function, but I'm trying to find out whether a class has a member type.

In the example below, both evaluate to "false", but I would like to find a way so that has_bar<foo1>::value evaluates to false, and has_bar<foo2>::value evaluates to true.

Is this possible?

#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;
}

Edit: implementing a specialisation in response to the answers below:

...if you use C::bar in the target template, the template will be discarded automatically for types that don't have that nested type.

I have tried to do this, but am clearly missing something

#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;
}

解决方案

You cannot obtain a pointer to member to a type member:

template <typename C> static yes check( decltype(&C::bar) ) ;

The subexpression &C::bar will only be valid when bar is a non-type member of C. But what you need to check is whether it is a type. A minimal change to your template could be:

template <typename C> static yes check( typename C::bar* ) ;

If bar is a nested type of C, then that function overload will be a valid candidate (the 0 will be a pointer to whatever C::bar type is), but if C does not contain a nested bar then it will be discarded and the second test will be the only candidate.

There is a different question as of whether the trait is needed at all, since if you use C::bar in the target template, the template will be discarded automatically for types that don't have that nested type.


EDIT

What I meant is that in your approach you need to create a trait for each and every possible nested type, just to generate a template that does or does not hold a nested type (enable_if). Let's take a different approach... First we define a general utility to select a type based on a condition, this is not required for this problem, and a simpler template <typename T> void_type { typedef void type; }; would suffice, but the utility template can be useful in other cases:

// 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;
};

Now se just need to use SFINAE for class template specializations:

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`
};

Note that the main difference with your approach is the use of an extra intermediate template (the one for which Substitution will Fail --and Is Not An Error) that yields a void type (on success). This is the reason why the void_type template above would also work: you just need to use the nested type as argument to a template, and have that fail, you don't really care what the template does, as long as the evaluation is a nested type (that must be void) if it succeeds.

In case it is not obvious (it wasn't at first for me) why your approach doesn't work, consider what the compiler needs to do when it encounters target<foo2>: The first step is finding that there is a template called target, but that template takes two arguments of which only one was provided. It then looks in the base template (the one that is not specialized) and finds that the second argument can be defaulted to void. From this point on, it will consider your instantiation to be: target<foo2,void> (after injecting the defaulted argument). And it will try to match the best specialization. Only specializations for which the second argument is void will be considered. Your template above will only be able to use the specialized version if T::bar is void (you can test that by changing foo2 to: struct foo2 { typedef void bar; }. Because you don't want the specialization to kick in only when the nested type is void you need the extra template that will take C::bar (and thus fail if the type does not contain a nested bar) but will always yield void as the nested type.

这篇关于可以使用type_traits / SFINAE来查找类是否定义了成员TYPE?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆