最近我经常遇到这个问题,我必须编写一个将输入作为const引用的函数。但在某些时候,这个函数(通常是一个构造函数)调用另一个可以使用输入作为移动参考的函数。出于这个原因,我通常创建一个函数的副本,以允许const引用和移动引用,即
#include <iostream>
class A {};
void foo(const A& a) { std::cout << "Reference" << std::endl; }
void foo( A&& a) { std::cout << "Move" << std::endl; }
void bar(const A& a) {
//Other things, which treat "a" as a const reference
foo(std::move(a));
}
void bar(A&& a) {
//Other things, which treat "a" as a const reference
foo(std::move(a));
}
int main() {
A a;
bar(a);
bar(A());
}
然而,将bar
复制两次显然非常难看,唯一的区别是签名和std::move
。我知道的一个替代方法是使bar
成为一个tempalte函数并使用std::forward
,但我不想这样做,因为它允许将任何参数类型传递给bar
(特别是因为在我的实际应用程序栏中是隐式构造函数)。
所以我的问题是:有没有其他方法可以通过函数转发移动引用而无需编写两次?
答案 0 :(得分:4)
如果要在单个函数中同时接受rvalues和lvalues,保留恢复其参数的值类别的可能性,则可以使用 forwarding-reference 。您可以使用表达式SFINAE 技术轻松限制传递的参数类型,在您的情况下,这将验证调用foo(std::forward<T>(a))
是否格式正确。如果不是,那么在重载决策期间,该函数将被排除在可行函数集之外:
在尾随返回类型中隐藏表达式SFINAE :
template <typename T>
auto bar(T&& a)
-> decltype(void(foo(std::forward<T>(a))))
{
//Other things, which treat "a" as a const reference
foo(std::forward<T>(a));
}
在模板参数列表中隐藏表达式SFINAE :
template <typename T,
typename = decltype(foo(std::forward<T>(std::declval<T&>())))>
void bar(T&& a)
{
//Other things, which treat "a" as a const reference
foo(std::forward<T>(a));
}
后一种方法对于构造函数(不指定返回类型)特别有用:
struct Bar
{
template <typename T,
typename = decltype(foo(std::forward<T>(std::declval<T&>())))>
Bar(T&& a)
{
foo(std::forward<T>(a));
}
};
答案 1 :(得分:2)
SFINAE的完美转发工作,并保持过载设置未受污染。您首先必须确定应该检查什么,例如应调用此模板的类型集或应该有效的表达式 在这里,两者都足够 - 这段代码检查类型:
// Helper template:
template <typename T, typename U, typename R=void>
using enable_if_compatible = typename std::enable_if<std::is_same<U,
typename std::remove_cv<
typename std::remove_reference<T>::type>::type>::value, R>::type;
// Possible usage:
template <typename T>
enable_if_compatible<T, A> bar(T&& a)
{
//Other things, which treat "a" as a const reference
foo(std::forward<T>(a));
}
Demo。
以下内容取决于对foo
的调用的有效性,并且应该更加灵活。
template <typename T>
auto bar(T&& a) -> decltype(void(foo(std::forward<T>(a))))
{
//Other things, which treat "a" as a const reference
foo(std::forward<T>(a));
}
答案 2 :(得分:1)
一个选项是添加bar
的模板化版本,该版本带有指向foo
的指针,并包含当前在bar
的现有实现中的所有公共代码。
template<class T>
void bar(T&& a, void (*f)(T&&a))
{
//Other things, which treat "a" as a const reference
f(std::move(a));
}
void bar(const A& a)
{
bar<const A&>(a, foo);
}
void bar(A&& a)
{
bar(std::move(a), foo);
}