右值或左值(const)引用形参
rvalue or lvalue (const) reference parameter
我想通过r值或l值(const)引用将参数(某些具体类型,例如int
)传递给成员函数。我的解决方案是:
#include <type_traits>
#include <utility>
struct F
{
using desired_parameter_type = int;
template< typename X, typename = typename std::enable_if< std::is_same< typename std::decay< X >::type, desired_parameter_type >::value >::type >
void operator () (X && x) const
{
// or even static_assert(std::is_same< typename std::decay< X >::type, desired_parameter_type >::value, "");
std::forward< X >(x); // something useful
}
};
另一个例子是http://pastebin.com/9kgHmsVC.
但是太啰嗦了。如何用更简单的方法来做呢?
也许我应该用std::remove_reference
和std::remove_const
的叠加而不是std::decay
,但这里只是一个简化。
如果我正确理解您的问题,您希望有一个函数,其参数是右值引用(在提供右值的情况下)或左值引用const
(在提供左值的情况下)。
但是这个函数能做什么呢?好吧,因为它必须能够处理这两种情况,包括提供左值的情况,它不能修改它的输入(至少不能修改绑定到x
参数的输入)——如果它这样做了,它将违反const
引用的语义。
但是,如果不能改变形参的状态,就没有理由允许右值引用:让x
始终是对const
的左值引用。对const
的左值引用可以绑定到右值,所以你可以同时传递左值和右值。
如果函数的语义根据传递的内容不同而不同,那么我会说写两个这样的函数更有意义:一个接受右值引用,一个接受左值引用到const
。
正如Andy所提到的,这里重要的是你可以在函数中做什么才有意义。
- 可以将参数转发给另一个函数。在这种情况下,使用模板并不重要,因为如果给出了错误的形参类型,它仍然会产生编译错误(将错误的类型发送给第二个函数)。您可以使用
template <typename T> blah (T && x)
。 - 其他任何东西都需要你根据它是r值引用还是l值引用来编写不同的代码,所以你无论如何都需要编写2个函数:
blah (const int & x)
和blah (int && x)
。
我假设您一定在尝试第一个选项,并且您正在尝试使任何潜在的编译器错误更加用户友好。好吧,我会说这不值得;程序员仍然会在任何像样的编译器的输出中看到一个"called by…"列表。
实际上,这是一个非常好的问题。到目前为止,我也一直在使用通用参考技巧加上enable_if
锤子。这里我给出了一个不使用模板的解决方案,它使用左值强制转换作为替代方案。
下面是一个真实的例子,其中使用已知的就地使用ofstream
的例子,这在c++ 98中是不可能的(或很难)(我在示例中使用ostringstream
以使其更清楚)。
首先,您将看到一个左值引用函数,就像c++ 98中经常看到的那样。
#include<iostream>
#include<sstream>
struct A{int impl_;};
std::ostringstream& operator<<(std::ostringstream& oss, A const& a){
oss << "A(" << a.impl_ << ")"; // possibly much longer code.
return oss;
}
// naive C++11 rvalue overload without using templates
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
oss << "A(" << a.impl_ << ")"; // ok, but there is code repetition.
return oss;
}
int main() {
A a{2};
{// C++98 way
std::ostringstream oss;
oss << a;
std::cout << oss.str() << std::endl; // prints "A(2)", ok"
}
{// possible with C++11, because of the rvalue overload
std::cout << (std::ostringstream() << a).str() << std::endl; //prints "A(2)", ok
}
}
正如你在c++ 11中看到的,我们可以实现在c++ 98中不能实现的。那就是利用ostringstream
(或ofstream
)。现在是OP的问题,这两个重载看起来很相似,可以同时加入一个吗?
一种选择是使用通用引用(Ostream&&
),并可选择使用enable_if
来约束类型。不太优雅。
通过使用这个"真实世界"的例子,我发现如果想为左值ref和右值ref使用相同的代码,因为可能你可以将一个转换为另一个!
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
return operator<<(oss, a);
}
这看起来像一个无限递归函数,但它不是因为oss
是一个左值引用(是的,它是一个左值引用,因为它有一个名字)。所以它会调用另一个重载。
你仍然需要编写两个函数,但其中一个函数的代码不需要维护。
总之,如果"it makes sense"©将一个函数同时应用于(非const)左值引用和右值,这也意味着你可以将右值转换为左值,因此你转发到一个函数。请注意,"它是有意义的"取决于上下文和预期代码的含义,我们必须通过显式调用左值重载来"告诉"编译器。
我并不是说这比使用通用引用更好,我是说这是另一种选择,而且可以说意图更明确。
可编辑的代码:http://ideone.com/XSxsvY。(欢迎反馈)
- 非类型引用形参/实参
- 将右值引用形参强制转换为右值引用
- 给引用形参赋值使对象无效
- 是否有必要在定义中使用模板形参来引用同一个类?
- 指向成员变量的引用/指针作为模板形参
- 修改const形参引用
- 为什么模板非类型形参指针和引用实参需要是全局的
- 右值或左值(const)引用形参
- 将所有引用形参设为const的理由是什么?
- 我可以重用右值引用形参来返回右值引用吗?
- const引用形参的默认值
- map带有const模板形参和const引用模板形参
- 为什么c++中通过引用传递的形参不需要解引用操作符
- NULL直接传递给需要const引用形参的函数(vc++ 4.2)
- c++引用形参函数
- 函数引用形参的默认值
- 将函数输出直接传递给接受引用形参的函数
- 避免非const引用形参
- 如何访问const引用形参的值
- 可以基于引用形参类型重载构造函数吗?