【问题标题】:rvalue or lvalue (const) reference parameterrvalue 或 lvalue (const) 参考参数
【发布时间】:2013-05-04 16:35:57
【问题描述】:

我想通过 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_referencestd::remove_const 的叠加而不是std::decay,但这里只是简化了。

【问题讨论】:

  • 通过右值传递像int 这样的简单类型是没有意义的。在更一般的情况下,我相信 C++11 对此有一个特殊情况,其中右值参数将被读取为右值 左值参数。不过,我不记得适用的确切情况。
  • 所以您希望x 成为右值引用(如果传递了右值引用)或对const 的左值引用?我的理解是否正确,X&amp;&amp; 不会被您接受,因为当传递左值时,它将是对非const 的左值引用?
  • 啊,我们开始吧,我相信您正在尝试这样做:thbecker.net/articles/rvalue_references/section_07.html,这将在下一页使用右值进行解释。如果我误解了这个问题,请大喊。
  • @AndyProwl 我的问题是如何提供一些 concrete 类型但具有非特定“参考性”的参数。
  • @Dukales:我跟不上你。如果你只是做template&lt;typename X&gt; void operator () (X&amp;&amp; x)被允许传递任何参数(左值或右值)。给定U 类型的参数,如果参数是右值,类型推导将使x 成为U&amp;&amp; x,如果参数是左值,则类型推导将成为U&amp; x

标签: c++ c++11 sfinae universal-reference


【解决方案1】:

如果我正确理解您的问题,您希望有一个函数,其参数是右值引用(如果提供了右值)或对const 的左值引用(如果提供了左值)。

但是这个函数会做什么呢?好吧,因为它必须能够处理这两种情况,包括提供左值的情况,所以它不能修改它的输入(至少不是绑定到x 参数的那个)——如果这样做,它将违反语义const 的引用。

但话又说回来,如果它不能改变参数的状态,就没有理由允许右值引用:而是让x 一直是对const 的左值引用。对const 的左值引用可以绑定到右值,因此您可以同时传递右值和左值。

如果函数的语义根据传递的内容而有所不同,那么我会说编写两个这样的函数更有意义:一个接受右值引用,一个接受左值引用参考const

【讨论】:

  • 常量是可选的(所以所有&amp;&amp;&amp;const &amp;都允许)
  • @Dukales:我不明白 :( 你什么意思?如果函数必须能够使用const 对象,这意味着它不会尝试修改x。但是话又说回来,这意味着您不需要右值引用
  • 你了解元编程吗?让我们假设,传递参数的函数将决定如何处理(如何调度)它们特定的“引用”和常量。
  • @Dukales:我知道元编程,是的。但我很想知道您如何想象这样的功能如何工作。假设您有一种方法可以按照您的意愿编写函数的签名,您希望在这样的函数的 body 中写入什么?
  • 我想在构造函数里面写一个初始化列表。
【解决方案2】:

正如 Andy 所提到的,重要的是你可以在你的函数中实际什么,这是有意义的。

  • 您可以将参数转发给另一个函数。在这种情况下,使用模板无关紧要,因为如果给出了错误的参数类型,它仍然会产生编译错误(错误的类型发送到第二个函数)。您可以使用template &lt;typename T&gt; blah (T &amp;&amp; x)
  • 其他任何事情都需要您编写不同的代码,具体取决于它是 r 值还是 l 值引用,因此无论如何您都需要编写 2 个函数:blah (const int &amp; x)blah (int &amp;&amp; x)

我假设您必须尝试第一个选项,并且您正在尝试使任何潜在的编译器错误更加用户友好。好吧,我会说这不值得;程序员仍然会在任何体面的编译器的输出中看到“由…调用”列表。

【讨论】:

  • 函数下可以表示具有海量初始化列表的ctor。
  • @Dukales:但这只是转发,所以您可以使用模板,但如果调用错误,仍然会出现编译器错误。
【解决方案3】:

其实这是一个很好的问题。到目前为止,我也一直在使用通用参考技巧加上enable_if 锤子。在这里,我提出了一个不使用模板并使用左值转换作为替代方案的解决方案。

下面是一个真实的例子,使用已知的 inplace 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&amp;&amp;),并且可以选择使用 enable_if 来约束类型。不是很优雅。

通过使用这个“真实世界”示例,我发现如果想对左值引用和右值引用使用相同的代码是因为你可能可以将一个转换为另一个!

std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
    return operator<<(oss, a);
}

这看起来像一个无限递归函数,但这并不是因为oss 是一个左值引用(是的,它是一个左值引用,因为它有一个名称)。所以它会调用另一个重载。

您仍然需要编写两个函数,但其​​中一个函数的代码不必维护。

总之,如果“有意义”©将函数同时应用于(非 const)左值引用和右值,这也意味着您可以将右值转换为左值,因此您可以转发到单个函数。请注意,“有意义”取决于上下文和预期代码的含义,我们必须通过显式调用左值重载来“告诉”编译器。

我并不是说这比使用通用参考更好,我说这是一种替代方法,并且可以说意图更明确。

此处可编辑代码:http://ideone.com/XSxsvY。 (欢迎反馈)

【讨论】:

    猜你喜欢
    • 2015-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-28
    • 2011-09-12
    • 2021-07-12
    • 2020-07-13
    相关资源
    最近更新 更多