SFINAE与可变模板 [英] SFINAE with variadic templates
问题描述
我对模板编程有点陌生,所以这可能是一个愚蠢的问题。我试图使用可变参数模板来检查一个类是否有一个成员(称为成员
)。为此,我写了类
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屋!