15

我见过这个问题,它允许人们检查成员函数的存在,但我试图找出一个类是否有成员类型

在下面的示例中,两者都评估为“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;
}
4

4 回答 4

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 };
};
于 2012-08-05T13:54:54.750 回答
13

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

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

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

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

如果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;
};

现在 se 只需将 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`
};

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

如果不是很明显(起初对我来说不是)为什么您的方法不起作用,请考虑编译器遇到时需要做什么target<foo2>:第一步是发现有一个名为 的模板target,但是该模板接受两个参数,其中只提供了一个。然后它查看基本模板(未专门化的模板)并发现第二个参数可以默认为void. 从这一点开始,它将认为您的实例化为:(target<foo2,void>在注入默认参数之后)。它会尽量匹配最好的专业。仅考虑第二个参数 void针对的特化。T::bar如果是,您上面的模板将只能使用专用版本void(您可以通过更改为测试foo2总是作为嵌套类型产生。struct foo2 { typedef void bar; }voidC::barbarvoid

于 2012-08-05T04:54:51.703 回答
1

我更喜欢将它包装在宏中。

测试.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);

测试.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;
}
于 2015-03-27T13:38:14.340 回答
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

于 2021-01-01T10:27:52.777 回答