模板元代码和私有成员

Template meta-code and private members

本文关键字:成员 代码      更新时间:2023-10-16

我想做这样的事情:

template <typename T>
class Foo
{
...
public:
    void DoSomething()
    {
        compile_time_if (T is ClassA)
        {
            m_T.DoThingOne();
            m_T.DoThingTwo();
        }
        DoSomeFooPrivateThing();
        m_T.DoThingThree();
    }
    T m_T;
};

在这种情况下,我知道所有有效的T都实现DoThingThree,但只有ClassA实现DoThingOneDoThingTwo。这不是鸭子类型的事情,我只想为ClassA做这额外的部分,我不想将这些方法添加到其他可能的T s中。我不能进行强制转换,因为可能的T s不是继承类型。

我知道我可以使用一个外部助手模板来适应这个:

template <typename T>
void Foo_DoSomething(T& t)
{
    t.DoThingThree();
}
template <>
void Foo_DoSomething(ClassA& t)
{
    t.DoThingOne();
    t.DoThingTwo();
    t.DoThingThree();
}
template <typename T>
class Foo
{
...
public:
    void DoSomething()
    {
        Foo_DoSomething(m_T);
    }
...
};

然而,现在这个外部模板无法访问Foo的私有成员(不能调用DoSomeFooPrivateThing),这限制了它的功能,而且它公开暴露在外部,这并不好看。(把外部方法变成朋友只会让事情变得更糟。)

另一个看似合理的选择是在内部实现:

template <typename T>
class Foo
{
...
public:
    void DoSomething()
    {
        DoSomethingImpl(m_T);
    }
...
private:
    template <typename T2>
    void DoSomethingImpl(T2& t)
    {
        DoSomeFooPrivateThing();
        t.DoThingThree();
    }
    template <>
    void DoSomethingImpl(ClassA& t)
    {
        t.DoThingOne();
        t.DoThingTwo();
        DoSomeFooPrivateThing();
        t.DoThingThree();
    }
...
};

但这需要复制外部模板类型和参数。这可能是可以接受的,但感觉还是有点奇怪。遗憾的是,它实际上并没有编译(至少在GCC中没有,因为它反对类内的专业化)。

有更好的方法吗?

我认为最后一个选项是最好的。

代替

template <>
void DoSomethingImpl(ClassA& t)
{
    t.DoThingOne();
    t.DoThingTwo();
    DoSomeFooPrivateThing();
    t.DoThingThree();
}

您可以使用(此处无需使用template):

void DoSomethingImpl(ClassA& t)
{
    t.DoThingOne();
    t.DoThingTwo();
    DoSomeFooPrivateThing();
    t.DoThingThree();
}

第一个解决方案:正如您所说,我们可以这样做:

template <typename T> class Foo{
    public:
    void doSomething(){
        doSomething(std::is_same<T,A>());
    }
    private:
    void doSomething(std::true_type){
        cout<<"A do"<<endl;
    }
    void doSomething(std::false_type){
        cout<<"any other do"<<endl;
    }
};

第二个解决方案:因为模板类Foo只有一个模板参数,所以我们可以直接进行这样的显式专门化。

template <typename T> class Foo{
    public:
     void doSomething(){
         cout<<"any other do..."<<endl;
     }
};
template<> void Foo<A>::doSomething(){
    cout<<"A do"<<endl;
}

Thrid解决方案:也许这不是一个好办法,我们可以这样做,这种方式(SFINAE)使用C++11或boost enable_if。当类型与A相同时,编译器会自动选择所需的类。

#include <iostream>
using namespace std;
class A {};
template <typename T,typename Enable = void>
class Foo
{
public:
    void DoSomething()
    {
        cout<<"anyother do"<<endl;
    }
private:
    T m_T;
};
template <typename T> class Foo<T, typename enable_if<is_same<T,A>::value>::type >
{
public:
    void DoSomething()
    {
        cout<<"A do"<<endl;
    }
private:
    T m_T;
};

更多:

如果两个Foo有很多相同的东西,我们可以为它创建这样的基类:

template <typename T> class BaseFoo{
 ...
};

和两个从BaseFoo派生的模板类,如下所示:

    template <typename T,typename Enable = void> 
class Foo:public BaseFoo<T>{...}

    template <typename T> class Foo<T, typename enable_if
<is_same<T,A>::value>::type >:public BaseFoo<T>{...}

我已经习惯了你的最后一个解决方案,所以它对我来说并不奇怪。
如果你喜欢,你可以一直做

    void DoSomethingImpl(T&t, std::true_type){...}

    void DoSomethingImpl(T&t, std::false_type){...}

然后

    DoSomethingImpl(m_T, std::is_same<T, ClassA>{});

这在功能上是等效的,但如果需要扩展功能,也可以进行更复杂的决策。