SFINAE与可变模板 [英] SFINAE with variadic templates

查看:157
本文介绍了SFINAE与可变模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对模板编程有点陌生,所以这可能是一个愚蠢的问题。我试图使用可变参数模板来检查一个类是否有一个成员(称为成员)。为此,我写了类
has_member

  #include< iostream> 
using namespace std;

class ClassWithMember
{
public:
int member;
};
class ClassWithoutMember
{
};

template< typename T>
class has_member
{
template< typename ... C>
class tester:public std :: false_type
{

};
template< typename First>
class tester< First>:public std :: true_type
{
void tester_fn(decltype(First :: member));
};

public:
enum {value = tester< T> :: value};
};

template< typename T1>
void my_function(const std :: enable_if_t< has_member< T1> :: value,T1>& obj)
{
cout< ; endl;
}

template< typename T1>
void my_function(const std :: enable_if_t<!has_member< T1> :: value,T1>& obj)
{
cout< < endl;
}

int main()
{
ClassWithMember objWithMember;
ClassWithoutMember objWithoutMember;
my_function< ClassWithMember> (objWithMember);
my_function< ClassWithoutMember> (objWithoutMember);
}



我希望通过SFINAE,该成员将静默失败并回退到一般模板。但我得到的错误:

  trial.cpp:在实例化'class has_member< ClassWithoutMember> :: tester< ClassWithoutMember& :
trial.cpp:28:10:需要从'class has_member< ClassWithoutMember>'
trial.cpp:38:41:required by'template< class T1& void my_function(std :: enable_if_t<(!has_member< T1> :: value),T1>&)[with T1 = ClassWithoutMember]'
trial.cpp:49:54:required from here
test.cpp:24:14:error:'member'不是'ClassWithoutMember'的成员
void tester_fn(decltype(First :: member));


解决方案

SFINAE仅适用于 / strong>的替换。在之外的替换失败是一个错误。这是你遇到的问题:

  has_member< ClassWithoutMember> :: value // error 
has_member
的声明中,或者是

tester ,它出现在定义中。这太晚了。你需要更早推动它。您可以使用 void_t 将其推入 has_member 的特殊化:

  template< typename ... T> 
struct make_void {using type = void; };

template< typename ... T>
using void_t = typename make_void< T ...> :: type;

template< typename T,typename = void>
struct has_member:std :: false_type {};

template< typename T>
struct has_member< T,void_t< decltype(T :: member)>> :std :: true_type {};

现在,如果没有 T :: member ,替换失败将发生在替换的直接上下文中,同时尝试选择 has_member 的正确的特殊化。该替换失败不是错误,特定的专门化将被丢弃,我们最终得到 false_type






另一方面,您使用 enable_if_t 防止模板扣除。你应该更喜欢这样写:

 模板< typename T1,
std :: enable_if_t< has_member< T1> :: value> * = nullptr>
void my_function(const T1& obj){...}

模板< typename T1,
std :: enable_if_t<!has_member< T1> :: value> * = nullptr>
void my_function(const T1& obj){...}

只要写:

  my_function(objWithMember); 
my_function(objWithoutMember);


I am somewhat new to template programming, so this might be a dumb question. I am trying to use variadic templates to check whether a class has a member (called member) or not. To do this, I have written the class has_member.

#include <iostream>
using namespace std;

class ClassWithMember
{
    public:
    int member;
};
class ClassWithoutMember
{
};

template <typename T>
class has_member
{
    template <typename... C>
    class tester: public std::false_type
    {

    };
    template <typename First>
    class tester<First>: public std::true_type
    {
        void tester_fn(decltype(First::member));
    };    

public:
    enum { value = tester<T>::value };
};

template<typename T1>
void my_function(const std::enable_if_t<has_member<T1>::value, T1> &obj)
{
    cout<<"Function for classes with member"<<endl;
}

template<typename T1>
void my_function(const std::enable_if_t<!has_member<T1>::value, T1> &obj)
{
    cout<<"Function for classes without member"<<endl;
}

int main()
{
    ClassWithMember objWithMember;
    ClassWithoutMember objWithoutMember;
    my_function<ClassWithMember> (objWithMember);
    my_function<ClassWithoutMember> (objWithoutMember);
}

I was expecting that by SFINAE, the substitution of the specialized template with classes without the member would fail silently and fall back to the general template. But I get the error:

trial.cpp: In instantiation of ‘class has_member<ClassWithoutMember>::tester<ClassWithoutMember>’:
trial.cpp:28:10:   required from ‘class has_member<ClassWithoutMember>’
trial.cpp:38:41:   required by substitution of ‘template<class T1> void my_function(std::enable_if_t<(! has_member<T1>::value), T1>&) [with T1 = ClassWithoutMember]’
trial.cpp:49:54:   required from here
trial.cpp:24:14: error: ‘member’ is not a member of ‘ClassWithoutMember’
     void tester_fn(decltype(First::member));

解决方案

SFINAE only applies in the immediate context of the substitution. Substitution failure outside of that is an error. That's the issue you're running into:

has_member<ClassWithoutMember>::value // error

That's because the substitution failure doesn't occur in the declaration of has_member or tester, it occurs in the definition. That is too late. You need to push it much earlier. You can use void_t to push it into the specialization of has_member:

template <typename... T>
struct make_void { using type = void; };

template <typename... T>
using void_t = typename make_void<T...>::type;

template <typename T, typename = void>
struct has_member : std::false_type { };

template <typename T>
struct has_member<T, void_t<decltype(T::member)>> : std::true_type { };

Now, if there is no T::member, the substitution failure will occur in the immediate context of the substitution while trying to pick the correct specialization of has_member. That substitution failure is not an error, that particular specialization would just be discarded and we end up with false_type as desired.


As a side-note, the way you're using your enable_if_t prevents template deduction. You should prefer to write it this way:

template <typename T1,
          std::enable_if_t<has_member<T1>::value>* = nullptr>
void my_function(const T1& obj) { ... }

template <typename T1,
          std::enable_if_t<!has_member<T1>::value>* = nullptr>
void my_function(const T1& obj) { ... }

That would let you just write:

my_function(objWithMember);
my_function(objWithoutMember);

这篇关于SFINAE与可变模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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