可以使用type_traits/SFINAE来查找类是否定义成员TYPE?

Ste*_*mer 14 c++ sfinae template-meta-programming c++11

我已经看到这个问题允许人们检查是否存在成员函数,但我试图找出一个类是否有成员类型.

在下面的示例中,两者都评估为"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;
}
Run Code Online (Sandbox Code Playgroud)

编辑:实施专业化以回应以下答案:

...如果在目标模板中使用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;
}
Run Code Online (Sandbox Code Playgroud)

Dir*_*ple 15

试试这个

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 };
};
Run Code Online (Sandbox Code Playgroud)

  • 它以`void_t`的名称成为标准 (2认同)

Dav*_*eas 13

您无法获取指向类型成员的成员的指针:

template <typename C> static yes check( decltype(&C::bar) ) ;
Run Code Online (Sandbox Code Playgroud)

子表达式&C::bar仅在bar非类型成员时才有效C.但你需要检查的是它是否是一种类型.对模板的最小更改可能是:

template <typename C> static yes check( typename C::bar* ) ;
Run Code Online (Sandbox Code Playgroud)

如果bar是嵌套类型C,那么该函数重载将是一个有效的候选者(0将是指向任何C::bar类型的指针),但如果C不包含嵌套,bar那么它将被丢弃,第二个测试将是唯一的候选者.

关于是否需要特征存在一个不同的问题,因为如果C::bar在目标模板中使用,则对于没有该嵌套类型的类型,模板将被自动丢弃.


编辑

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

// 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;
};
Run Code Online (Sandbox Code Playgroud)

现在只需要使用SFINAE进行类模板特化:

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`
};
Run Code Online (Sandbox Code Playgroud)

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

如果不明显(这对我来说不是)为什么你的方法不起作用,请考虑编译器在遇到时需要做什么target<foo2>:第一步是发现有一个名为的模板target,但该模板有两个参数,其中只提供了一个参数.然后它查找基本模板(非专用的模板)并发现第二个参数可以默认为void.从这一点开始,它将考虑您的实例化:( target<foo2,void>在注入默认参数之后).它会尝试匹配最好的专业化.只有为其第二个参数专长 void将予以考虑.您的模板上面只能使用专门的版本,如果T::barvoid(你可以通过更改测试foo2到:struct foo2 { typedef void bar; }因为你不希望专业化踢只有当嵌套类型是void你所需要的额外的模板,将采取C::bar(因此,如果类型不包含嵌套bar,则会失败,但总是会产生void嵌套类型.

  • @lori:大卫并不是说需要一个特性,只是在你的特定用例中可能不需要它 - 在很多情况下,特性恰恰是合适的解决方案,但在其他情况下它可能是矫枉过正的.例如,请参阅[此演示](http://ideone.com/6gxtW),它不使用特征,而是使用简单的重载.如果你需要一个特性,[本演示](http://ideone.com/dhK9A)会显示一个特征. (2认同)