涵盖 const 和非 const 方法的 C++ 模板

标签 c++ templates visitor-pattern

我遇到了 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"; }
};

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

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);
    }
};

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

static void test()
{
    Aggregate a;
    a(VisitorRead());
    const Aggregate b(a);
    b(VisitorWrite());
}

现在,这里的问题是 Aggregate::operator() 对于 const 和非 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);
}

这意味着不需要 Aggregate::operator() 并且没有重复。但是我对 visit() 是通用的,没有提到 Aggregate 类型这一事实感到不舒服。

有没有更好的办法?

最佳答案

我倾向于喜欢简单的解决方案,所以我会选择自由函数的方法,可能会添加 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);   
}

如果您没有启用 C++0x 的编译器(或你可以从 boost type_traits 借用它们)

编辑:在编写 SFINAE 方法时,我意识到在 OP 中提供纯模板化(无 SFINAE)解决方案存在很多问题,其中包括如果您需要提供多个 visitable 类型,不同的模板会发生冲突(即它们会和其他模板一样好)。通过提供 SFINAE,您实际上只是为满足条件的类型提供 visit 函数,将奇怪的 SFINAE 转换为等价于:

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

关于涵盖 const 和非 const 方法的 C++ 模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7792052/

相关文章:

c++ - 是否可以创建一个位集 vector ?

c++ - 是否可以反向使用 boost fusion map,也就是 key 是 567,value 是 type ?

jQuery 自定义小部件 : how can I include a html template?

c++ - 模板化的 constexpr 变量

c++ - 我可以将一个类与模板中的另一个类关联起来(使用 C++17 变体)吗?

C++17 构建两侧具有变体的运算符的最佳方法?

c++ - Boost Asio async_send 产生蜂鸣声

c++ - 由于槽和信号的错误使用导致程序崩溃

c++ - 从共享内存存储和访问结构数组

c++ - 根据某些元数据在运行时转换参数包值