如何避免将类成员传递给回调的类似const和非const成员函数之间的代码重复?

时间:2015-08-05 17:25:41

标签: c++ c++11 const code-duplication

这是this question的特定情况,其中that answer无效。

Consider

struct hurg {};

class furg {
public:
    template <class F>
    void for_each_hurg(F&& f) const {
        for (auto& h : hurgs) {
            f(h);
        }
    }

    template <class F>
    void for_each_hurg(F&& f) {
        for (auto& h : hurgs) {
            f(h);
        }
    }
private:
    std::vector<hurg> hurgs;
};

用法:

furg f;
const auto& cf = f;

f.for_each_hurg([](hurg& h) { });
cf.for_each_hurg([](const hurg& h) { });

const和非const版本的代码完全相同,但仅仅因为auto& h在第一种情况下推断const hurg&而在hurg&中推断template <class F> void for_each_hurg(F&& f) { const_cast<const furg&>(*this).for_each_hurg([&f](const hurg& h) { f(const_cast<hurg&>(h)); }); } 第二种情况。

本着以前联系到Scott Meyers' solution的精神,我想出了以下内容:

repmat

然而,这似乎比它的价值更麻烦,特别是如果类型很长并且我不能使用C ++ 14的通用lambdas。

1 个答案:

答案 0 :(得分:3)

您可以使用静态成员函数模板将*this转发到通用函数参数:

template<typename Self, typename F>
static void for_each_hurg(Self& s, F&& f) {
    for (auto& h : s.hurgs) {
        f(h);
    }
}

template<typename F>
void for_each_hurg(F&& f) { for_each_hurg(*this, forward<F>(f))); }

template<typename F>
void for_each_hurg(F&& f) const { for_each_hurg(*this, forward<F>(f))); }

自成员函数的引用限定符出现以来,一般的解决方案是完美转发*this。这并不总是重要的,因为您通常不希望在rvalues上调用成员函数。我将在此处添加此内容,因为我认为它是更通用解决方案的一部分。

不幸的是,*this始终是左值,因此您需要在成员函数包装器中进行额外的手动操作:

template<typename Self, typename F>
static void for_each_hurg(Self&& s, F&& f) {
    /* ... */
}

template<typename F>
void for_each_hurg(F&& f) && { for_each_hurg(move(*this), forward<F>(f))); }

template<typename F>
void for_each_hurg(F&& f) & { for_each_hurg(*this, forward<F>(f))); }

遗憾的是,这不对称:(

也可以通过朋友功能模板实现上述功能。这有两个好处:

  • 您可以将友元函数模板移动到命名空间作用域,在furg作为类模板的情况下,减少了编译器必须处理的函数模板的数量(每个类模板一个,而不是每个模板一个)实例化)。但这通常需要一些样板代码和前向声明。
  • 您可以使用相同的名称调用该函数作为自由函数或成员函数,例如furg f; for_each_hurg(furg, [](hurg){}); furg.for_each_hurg([](hurg){});(当非限定查找找到成员函数时,它不会执行/忽略ADL的结果。因此,您必须将友元函数放在命名空间范围内,以便能够引用它通过非静态成员函数包装器中的qualified-id。)

此外,您必须保护该功能模板不被贪婪;将其放入某些namespace detail或添加enable-if子句。这可能不值得努力。