C++模板覆盖const和非const方法

pap*_*jam 16 c++ templates visitor

我有重复相同代码const和非const版本的问题.我可以用一些代码来说明问题.这里有两个样本访问者,一个修改访问对象,另一个不访问.

struct VisitorRead 
{
    template <class T>
    void operator()(T &t) { std::cin >> t; }
};

struct VisitorWrite 
{
    template <class T> 
    void operator()(const T &t) { std::cout << t << "\n"; }
};
Run Code Online (Sandbox Code Playgroud)

现在这里是一个聚合对象 - 这只有两个数据成员,但我的实际代码要复杂得多:

struct Aggregate
{
    int i;
    double d;

    template <class Visitor>
    void operator()(Visitor &v)
    {
        v(i);
        v(d);
    }
    template <class Visitor>
    void operator()(Visitor &v) const
    {
        v(i);
        v(d);
    }
};
Run Code Online (Sandbox Code Playgroud)

并且有一个函数来演示以上内容:

static void test()
{
    Aggregate a;
    a(VisitorRead());
    const Aggregate b(a);
    b(VisitorWrite());
}
Run Code Online (Sandbox Code Playgroud)

现在,这里的问题是Aggregate::operator()for const和non const版本的重复.

是否有可能避免重复此代码?

我有一个解决方案是这样的:

template <class Visitor, class Struct>
void visit(Visitor &v, Struct &s) 
{
    v(s.i);
    v(s.i);
}

static void test2()
{
    Aggregate a;
    visit(VisitorRead(), a);
    const Aggregate b(a);
    visit(VisitorWrite(), b);
}
Run Code Online (Sandbox Code Playgroud)

这意味着既不Aggregate::operator()需要也不重复.但我不满意visit()没有提及类型的通用事实Aggregate.

有没有更好的办法?

Dav*_*eas 9

我倾向于喜欢简单的解决方案,所以我会选择自由函数方法,可能会添加 SFINAE 来禁用除以下类型之外的函数Aggregate

template <typename Visitor, typename T>
typename std::enable_if< std::is_same<Aggregate,
                                   typename std::remove_const<T>::type 
                                  >::value
                       >::type
visit( Visitor & v, T & s ) {  // T can only be Aggregate or Aggregate const
    v(s.i);
    v(s.d);   
}
Run Code Online (Sandbox Code Playgroud)

Where enable_ifis_same并且remove_const如果您没有启用 C++0x 的编译器(或者您可以从 boost type_traits 借用它们),则实际上很容易实现

编辑:在编写 SFINAE 方法时,我意识到在 OP 中提供简单模板化(无 SFINAE)解决方案存在很多问题,其中包括如果您需要提供多个可访问类型,不同的模板将碰撞(即他们会和其他人一样匹配)。通过提供 SFINAE,您实际上visit只为满足条件的类型提供函数,将奇怪的 SFINAE 转换为等效于:

// pseudocode, [] to mark *optional*
template <typename Visitor>
void visit( Visitor & v, Aggregate [const] & s ) {
   v( s.i );
   v( s.d );
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*sop 5

struct Aggregate
{
    int i;
    double d;

    template <class Visitor>
    void operator()(Visitor &v)
    {
        visit(this, v);
    }
    template <class Visitor>
    void operator()(Visitor &v) const
    {
        visit(this, v);
    }
  private:
    template<typename ThisType, typename Visitor>
    static void visit(ThisType *self, Visitor &v) {
        v(self->i);
        v(self->d);
    }
};
Run Code Online (Sandbox Code Playgroud)

好的,所以仍然有一些样板,但没有重复的代码依赖于Aggregate的实际成员.与const_cast(例如)Scott Meyers倡导的避免重复的方法不同,编译器将确保两个公共函数的const正确性.

  • 实际上,谁在乎"ThisType"可以是什么?显然,它只能是与`Aggregate`具有相同成员的东西,因为`visit`的主体将使用这些成员.最糟糕的现实情况,它是一个派生类`Aggregate`,我们无法访问派生类添加的任何额外成员.但是,当您尝试使用类层次结构执行此访问者时,这是预期的,子类必须确保隐藏/覆盖所有内容.如果有人找到一种方法一般地使用`visit`,在其他一些具有相同成员名称的类上,那么祝他们好运;-) (2认同)