如何避免将 class 成员传递给回调的类似 const 和非常量成员函数之间的代码重复?
How to avoid code duplication between similar const and non-const member functions which pass class members to callbacks?
这是this question where that answer的一个特例,不能直接起作用。
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&
在第二种情况下。
本着之前链接到 Scott Meyers' solution 的精神,我想出了以下内容:
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));
});
}
然而,这似乎比它的价值更麻烦,尤其是当类型很长并且我不能使用 C++14 的通用 lambda 时。
您可以使用静态成员函数模板将 *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
。这并不总是很重要,因为您通常不希望成员函数在右值上被调用。我将在此处添加它,因为我认为它是更通用解决方案的一部分。
不幸的是,*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
是 class 模板的情况下,这减少了编译器必须处理的函数模板数量(每个 class 模板,而不是每个实例一个)。不过,这通常需要一些样板代码和前向声明。
- 您可以调用同名函数作为自由函数或成员函数,例如
furg f; for_each_hurg(furg, [](hurg){}); furg.for_each_hurg([](hurg){});
(当非限定查找找到成员函数时,它不会执行/忽略 ADL 的结果。因此,您必须将友元函数放在命名空间范围内,以便能够引用它通过非静态成员函数包装器中的合格 ID。)
此外,您必须保护该函数模板免于贪婪;通过将其放入某些 namespace detail
或添加 enable-if 子句。这可能不值得付出努力。
这是this question where that answer的一个特例,不能直接起作用。
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&
在第二种情况下。
本着之前链接到 Scott Meyers' solution 的精神,我想出了以下内容:
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));
});
}
然而,这似乎比它的价值更麻烦,尤其是当类型很长并且我不能使用 C++14 的通用 lambda 时。
您可以使用静态成员函数模板将 *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
。这并不总是很重要,因为您通常不希望成员函数在右值上被调用。我将在此处添加它,因为我认为它是更通用解决方案的一部分。
不幸的是,*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
是 class 模板的情况下,这减少了编译器必须处理的函数模板数量(每个 class 模板,而不是每个实例一个)。不过,这通常需要一些样板代码和前向声明。 - 您可以调用同名函数作为自由函数或成员函数,例如
furg f; for_each_hurg(furg, [](hurg){}); furg.for_each_hurg([](hurg){});
(当非限定查找找到成员函数时,它不会执行/忽略 ADL 的结果。因此,您必须将友元函数放在命名空间范围内,以便能够引用它通过非静态成员函数包装器中的合格 ID。)
此外,您必须保护该函数模板免于贪婪;通过将其放入某些 namespace detail
或添加 enable-if 子句。这可能不值得付出努力。