SFINAE:派生类隐藏基类函数取决于T

时间:2019-02-06 08:36:57

标签: c++ c++17 sfinae

我编写了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非常陌生,仍然可以通过https://stackoverflow.com/a/50562202/学习。

2 个答案:

答案 0 :(得分:3)

此代码有两个问题:

  1. 在调用bEn1.test();bEn2.test();时,编译器会发现名称test指向类B中的函数,并且重载函数集将仅包含{{1 }}。可以通过将名称从基类命名为派生类来解决此问题:
B::test
  1. 但是,现在非模板功能将比模板功能更受首选(即使template<EN T1> class B : public Base{ public: using Base::test; 工作时),因此enable_if将打印1。

为了使这项工作再次生效,您可以引入另一个类似于第一个示例的重载,该重载将从基类中调用函数,而不是将B<EN2> bEn2; bEn2.test();名称引入派生类:

Base::test

使用public: template<EN enLocal=T1> std::enable_if_t< enLocal!=EN2,void > test(){ return Base::test(); } 代替类型特征或SFINAE的另一种C ++ 17风格的变通方法:

if constexpr

答案 1 :(得分:1)

根据实际用例,您还可以考虑某种形式的标签分发:

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"
}