SFINAE:派生 class 隐藏基础 class 函数取决于 T
SFINAE : Derived class hide base class function depend on T
我编码了 2 个 SFINAE 片段。
他们做的完全一样。
但是,第一个有效,而第二个无效。
为什么? (第二个更接近我的真实程序。)
此代码有效(http://coliru.stacked-crooked.com/a/50e07af54708f076)
#include <iostream>
#include <type_traits>
enum EN{ EN1,EN2 };
template<EN T1> class B{
public: template<EN enLocal=T1> typename
std::enable_if<enLocal==EN1,void>::type test(){ std::cout<<"1"<<std::endl;}
public: template<EN enLocal=T1> typename
std::enable_if<enLocal==EN2,void>::type test(){ std::cout<<"2"<<std::endl; }
};
int main(){
B<EN1> b;
b.test();
}
但是这段代码是不可编译的(http://coliru.stacked-crooked.com/a/28b6afd443b36c7e) :-
#include <iostream>
#include <type_traits>
enum EN{ EN1,EN2 };
class Base{
public: void test(){
std::cout<<"1"<<std::endl;
};
};
template<EN T1> class B : public Base{
public: template<EN enLocal=T1>
std::enable_if_t< enLocal==EN2,void > test(){
std::cout<<"2"<<std::endl;
}
};
int main(){
B<EN1> bEn1; bEn1.test(); //should print 1
//B<EN2> bEn2; bEn2.test(); //print 2
}
我对 SFINAE 很陌生,仍在通过 学习它。
此代码有两个问题:
- 当调用
bEn1.test();
或 bEn2.test();
时,编译器会发现名称 test
指的是 class B
中的函数和重载函数集将仅包括 B::test
。这可以通过将名称从基础 class 引入派生的 class: 来解决
template<EN T1> class B : public Base{
public: using Base::test;
- 但是现在非模板函数将优先于模板函数(即使
enable_if
有效)所以 B<EN2> bEn2; bEn2.test();
将打印 1.
为了使这项工作再次起作用,您可以引入另一个类似于第一个示例中的重载,它将从基础 class 调用函数,而不是将 Base::test
名称引入派生 class:
public: template<EN enLocal=T1>
std::enable_if_t< enLocal!=EN2,void > test(){
return Base::test();
}
另一种可能的 C++17 风格的解决方法,利用 if constexpr
而不是类型特征或 SFINAE:
public: template<EN enLocal = T1> void
test()
{
if constexpr(EN2 == enLocal)
{
std::cout<<"2"<<std::endl;
}
else
{
Base::test();
}
}
根据实际用例,您还可以考虑某种形式的标签分发:
enum class En {
base, a, b, c
};
template<En Type> void test_impl()
{
if constexpr (Type == En::base)
std::cout << "Base\n";
else if constexpr (Type == En::a)
std::cout << "1\n";
else if constexpr (Type == En::b)
std::cout << "2\n";
else
std::cout << "Default\n";
}
struct Base {
void test() {
std::cout << "Base - ";
test_impl<En::base>();
}
};
template<En Type>
struct Derived : public Base {
void test() {
std::cout << "Derived - ";
test_impl<Type>();
}
};
int main()
{
Base b;
b.test(); // -> "Base - Base"
Derived<En::a> b1;
b1.test(); // -> "Derived - 1"
Derived<En::b> b2;
b2.test(); // -> "Derived - 2"
Derived<En::base> b3;
b3.test(); // -> "Derived - Base"
Derived<En::c> b4;
b4.test(); // -> "Derived - Default"
}
我编码了 2 个 SFINAE 片段。
他们做的完全一样。
但是,第一个有效,而第二个无效。
为什么? (第二个更接近我的真实程序。)
此代码有效(http://coliru.stacked-crooked.com/a/50e07af54708f076)
#include <iostream>
#include <type_traits>
enum EN{ EN1,EN2 };
template<EN T1> class B{
public: template<EN enLocal=T1> typename
std::enable_if<enLocal==EN1,void>::type test(){ std::cout<<"1"<<std::endl;}
public: template<EN enLocal=T1> typename
std::enable_if<enLocal==EN2,void>::type test(){ std::cout<<"2"<<std::endl; }
};
int main(){
B<EN1> b;
b.test();
}
但是这段代码是不可编译的(http://coliru.stacked-crooked.com/a/28b6afd443b36c7e) :-
#include <iostream>
#include <type_traits>
enum EN{ EN1,EN2 };
class Base{
public: void test(){
std::cout<<"1"<<std::endl;
};
};
template<EN T1> class B : public Base{
public: template<EN enLocal=T1>
std::enable_if_t< enLocal==EN2,void > test(){
std::cout<<"2"<<std::endl;
}
};
int main(){
B<EN1> bEn1; bEn1.test(); //should print 1
//B<EN2> bEn2; bEn2.test(); //print 2
}
我对 SFINAE 很陌生,仍在通过 学习它。
此代码有两个问题:
- 当调用
bEn1.test();
或bEn2.test();
时,编译器会发现名称test
指的是 classB
中的函数和重载函数集将仅包括B::test
。这可以通过将名称从基础 class 引入派生的 class: 来解决
template<EN T1> class B : public Base{
public: using Base::test;
- 但是现在非模板函数将优先于模板函数(即使
enable_if
有效)所以B<EN2> bEn2; bEn2.test();
将打印 1.
为了使这项工作再次起作用,您可以引入另一个类似于第一个示例中的重载,它将从基础 class 调用函数,而不是将 Base::test
名称引入派生 class:
public: template<EN enLocal=T1>
std::enable_if_t< enLocal!=EN2,void > test(){
return Base::test();
}
另一种可能的 C++17 风格的解决方法,利用 if constexpr
而不是类型特征或 SFINAE:
public: template<EN enLocal = T1> void
test()
{
if constexpr(EN2 == enLocal)
{
std::cout<<"2"<<std::endl;
}
else
{
Base::test();
}
}
根据实际用例,您还可以考虑某种形式的标签分发:
enum class En {
base, a, b, c
};
template<En Type> void test_impl()
{
if constexpr (Type == En::base)
std::cout << "Base\n";
else if constexpr (Type == En::a)
std::cout << "1\n";
else if constexpr (Type == En::b)
std::cout << "2\n";
else
std::cout << "Default\n";
}
struct Base {
void test() {
std::cout << "Base - ";
test_impl<En::base>();
}
};
template<En Type>
struct Derived : public Base {
void test() {
std::cout << "Derived - ";
test_impl<Type>();
}
};
int main()
{
Base b;
b.test(); // -> "Base - Base"
Derived<En::a> b1;
b1.test(); // -> "Derived - 1"
Derived<En::b> b2;
b2.test(); // -> "Derived - 2"
Derived<En::base> b3;
b3.test(); // -> "Derived - Base"
Derived<En::c> b4;
b4.test(); // -> "Derived - Default"
}