C++ 将函数对象作为左值 and/or 右值传递
C++ Passing function objects as lvalues and/or rvalues
我有一个 class 应该根据用户提供的谓词过滤其内容。我得到的接口规定引用谓词:
class Test {
vector<int> data;
public:
template <class PREDTYPE>
void filter(PREDTYPE& pred) {
return;
}
};
我也给了一段测试代码,大致是这样的:
class Filter {
public:
bool operator()(int) const {
return false;
}
};
int main() {
Test test;
test.filter(Filter());
}
这不编译,说cannot bind non-const lvalue reference of type 'Filter&' to an rvalue of type 'Filter'
。如果我将测试代码更改为
int main() {
Test test;
Filter filter;
test.filter(filter);
}
它有效,但这取决于最终用户,我无法控制他们的行为。我尝试重载 filter 方法,创建一个版本,该版本将按值接受谓词,然后通过引用传递它,但这也无法编译,并显示消息 call of overloaded 'filter(Filter&)' is ambiguous
.
因此我的问题是:是否可以构造一个过滤器来接受谓词的右值和左值?
您可能希望将您的成员函数签名更改为
template <class PREDTYPE> void filter(PREDTYPE&& pred)
此处,谓词作为 forwarding reference 传递,它折叠为左值引用,左值引用传入右值,右值引用传入右值。
简而言之,是的,它是 (C++11)
您只需要依靠引用折叠规则来确保这一点:
template <typename PREDTYPE>
void filter(PREDTYPE&& pred) { // notice the &&
// ... Whatever
// Use perfect forwarding *IF* you can
std::forward<PREDTYPE>(pred)(some_stuff);
// Do not use pred after it has been forwarded!
}
这将接受右值和左值,而不必依赖 const
- 引用(因此您的谓词仍然可以是可变的)。如果您坚持使用旧的 C++ 标准,最好的办法是改用 const 引用(上面突出显示警告)或将您的过滤器嵌入 std::function
并依赖调用站点的隐式转换。
通常,函数对象应该按值传递。所以不要超载并将您的签名更改为
template <class PREDTYPE> void filter(PREDTYPE pred)
可以找到为什么这样更好的解释 here。
这取决于函数对谓词的作用。如果它在 returns 之后保留 reference/pointer,则您必须提供足够长的生命周期。但是,如果它不保留传入的谓词对象的任何内存,您可以将谓词转换为左值:
test.filter(static_cast<Filter&>(Filter()));
或者用一个简短的实用函数包装该转换:
template <class T>
T& stay(T&& a)
{ return a; }
test.filter(stay(Filter()));
我有一个 class 应该根据用户提供的谓词过滤其内容。我得到的接口规定引用谓词:
class Test {
vector<int> data;
public:
template <class PREDTYPE>
void filter(PREDTYPE& pred) {
return;
}
};
我也给了一段测试代码,大致是这样的:
class Filter {
public:
bool operator()(int) const {
return false;
}
};
int main() {
Test test;
test.filter(Filter());
}
这不编译,说cannot bind non-const lvalue reference of type 'Filter&' to an rvalue of type 'Filter'
。如果我将测试代码更改为
int main() {
Test test;
Filter filter;
test.filter(filter);
}
它有效,但这取决于最终用户,我无法控制他们的行为。我尝试重载 filter 方法,创建一个版本,该版本将按值接受谓词,然后通过引用传递它,但这也无法编译,并显示消息 call of overloaded 'filter(Filter&)' is ambiguous
.
因此我的问题是:是否可以构造一个过滤器来接受谓词的右值和左值?
您可能希望将您的成员函数签名更改为
template <class PREDTYPE> void filter(PREDTYPE&& pred)
此处,谓词作为 forwarding reference 传递,它折叠为左值引用,左值引用传入右值,右值引用传入右值。
简而言之,是的,它是 (C++11)
您只需要依靠引用折叠规则来确保这一点:
template <typename PREDTYPE>
void filter(PREDTYPE&& pred) { // notice the &&
// ... Whatever
// Use perfect forwarding *IF* you can
std::forward<PREDTYPE>(pred)(some_stuff);
// Do not use pred after it has been forwarded!
}
这将接受右值和左值,而不必依赖 const
- 引用(因此您的谓词仍然可以是可变的)。如果您坚持使用旧的 C++ 标准,最好的办法是改用 const 引用(上面突出显示警告)或将您的过滤器嵌入 std::function
并依赖调用站点的隐式转换。
通常,函数对象应该按值传递。所以不要超载并将您的签名更改为
template <class PREDTYPE> void filter(PREDTYPE pred)
可以找到为什么这样更好的解释 here。
这取决于函数对谓词的作用。如果它在 returns 之后保留 reference/pointer,则您必须提供足够长的生命周期。但是,如果它不保留传入的谓词对象的任何内存,您可以将谓词转换为左值:
test.filter(static_cast<Filter&>(Filter()));
或者用一个简短的实用函数包装该转换:
template <class T>
T& stay(T&& a)
{ return a; }
test.filter(stay(Filter()));