gpt4 book ai didi

C++0x 右值引用 - 左值-右值绑定(bind)

转载 作者:IT老高 更新时间:2023-10-28 22:22:05 32 4
gpt4 key购买 nike

这是一个后续问题
C++0x rvalue references and temporaries

在上一个问题中,我问过这段代码应该如何工作:

void f(const std::string &); //less efficient
void f(std::string &&); //more efficient

void g(const char * arg)
{
f(arg);
}

由于隐式临时性,似乎应该调用移动重载,这发生在 GCC 而不是 MSVC(或 MSVC 的智能感知中使用的 EDG 前端)。

这段代码怎么样?
void f(std::string &&); //NB: No const string & overload supplied

void g1(const char * arg)
{
f(arg);
}
void g2(const std::string & arg)
{
f(arg);
}

看来,根据我之前问题的答案,函数 g1是合法的(并且被 GCC 4.3-4.5 接受,但不被 MSVC 接受)。然而,GCC 和 MSVC 都拒绝了 g2因为第 13.3.3.1.4/3 条禁止左值绑定(bind)到右值引用参数。我理解这背后的基本原理 - 在 N2831“使用右值引用修复安全问题”中对此进行了解释。我还认为 GCC 可能正在按照该论文作者的意图实现该条款,因为 GCC 的原始补丁是由其中一位作者(Doug Gregor)编写的。

但是,我不认为这是很直观的。对我来说,(a) const string &在概念上更接近于 string &&const char * , (b) 编译器可以在 g2 中创建一个临时字符串,好像是这样写的:
void g2(const std::string & arg)
{
f(std::string(arg));
}

事实上,有时复制构造函数被认为是一个隐式转换操作符。在语法上,这是通过复制构造函数的形式提出的,标准甚至在第 13.3.3.1.2/4 节中特别提到了这一点,其中派生基转换的复制构造函数被赋予比其他用户定义的更高的转换等级转换:

A conversion of an expression of class type to the same class type is given Exact Match rank, and a conversion of an expression of class type to a base class of that type is given Conversion rank, in spite of the fact that a copy/move constructor (i.e., a user-defined conversion function) is called for those cases.



(我假设这在将派生类传递给像 void h(Base) 这样的函数时使用,该函数按值获取基类。)

动机

我提出这个问题的动机类似于 How to reduce redundant code when adding new c++0x rvalue reference operator overloads 中提出的问题。 (“如何在添加新的 c++0x 右值引用运算符重载时减少冗余代码”)。

如果您有一个函数接受许多可能可移动的参数,并且会在可能的情况下移动它们(例如工厂函数/构造函数: Object create_object(string, vector<string>, string) 等),并且想要适本地移动或复制每个参数,您快速开始编写大量代码。

如果参数类型是可移动的,那么可以只编写一个按值接受参数的版本,如上所述。但是,如果参数是(传统的)不可移动但可交换的类,如 C++03,并且您无法更改它们,那么编写右值引用重载会更有效。

因此,如果左值确实通过隐式拷贝绑定(bind)到右值,那么您可以只编写一个重载,如 create_object(legacy_string &&, legacy_vector<legacy_string> &&, legacy_string &&)它或多或少会像提供右值/左值引用重载的所有组合一样工作——作为左值的实际参数将被复制然后绑定(bind)到参数,作为右值的实际参数将直接绑定(bind)。

澄清/编辑:我意识到这实际上与按值接受可移动类型的参数相同,例如 C++0x std::string 和 std::vector(除了概念上调用移动构造函数的次数)。但是,对于可复制但不可移动的类型,它并不相同,其中包括具有显式定义的复制构造函数的所有 C++03 类。考虑这个例子:
class legacy_string { legacy_string(const legacy_string &); }; //defined in a header somewhere; not modifiable.

void f(legacy_string s1, legacy_string s2); //A *new* (C++0x) function that wants to move from its arguments where possible, and avoid copying
void g() //A C++0x function as well
{
legacy_string x(/*initialization*/);
legacy_string y(/*initialization*/);

f(std::move(x), std::move(y));
}

g电话 f ,然后 xy将被复制 - 我不知道编译器如何移动它们。如 f反而被宣布为服用 legacy_string &&参数,它可以避免调用者显式调用的那些拷贝 std::move在论据上。我不明白这些是如何等效的。

问题

我的问题是:
  • 这是对标准的有效解释吗?无论如何,这似乎不是传统的或预期的。
  • 它具有直观意义吗?
  • 我没有看到这个想法有什么问题吗?似乎您可以在不完全预期的情况下悄悄创建拷贝,但这就是 C++03 中某些地方的现状。此外,它会使一些重载目前不可行,但在实践中我认为这不是问题。
  • 这是一个足够重要的改进,值得做吗? GCC 的实验性补丁?
  • 最佳答案

    What about this code?


    void f(std::string &&); //NB: No const string & overload supplied

    void g2(const std::string & arg)
    {
    f(arg);
    }

    ...However, GCC and MSVC both reject g2 because of clause 13.3.3.1.4/3, which prohibits lvalues from binding to rvalue ref arguments. I understand the rationale behind this - it is explained in N2831 "Fixing a safety problem with rvalue references". I also think that GCC is probably implementing this clause as intended by the authors of that paper, because the original patch to GCC was written by one of the authors (Doug Gregor)....



    不,这只是两个编译器拒绝您的代码的一半原因。另一个原因是您不能使用引用 const 对象的表达式初始化对非常量的引用。因此,即使在 N2831 之前,这也不起作用。根本不需要转换,因为字符串已经是字符串。看来你想用 string&&喜欢 string .然后,只需编写您的函数 f以便它按值获取字符串。如果你想让编译器创建一个 const 字符串左值的临时拷贝,这样你就可以调用一个带 string&& 的函数。 ,按值或按 rref 获取字符串不会有区别,是吗?

    N2831 与此场景关系不大。

    If you have a function that accepts a number of potentially-moveable arguments, and would move them if it can (e.g. a factory function/constructor: Object create_object(string, vector, string) or the like), and want to move or copy each argument as appropriate, you quickly start writing a lot of code.



    并不真地。为什么要编写大量代码?几乎没有理由用 const& 来弄乱您的所有代码/ &&重载。您仍然可以使用一个混合了传值和传引用到常量的单一函数——这取决于您想对参数做什么。至于工厂,想法是使用完美转发:
    template<class T, class... Args>
    unique_ptr<T> make_unique(Args&&... args)
    {
    T* ptr = new T(std::forward<Args>(args)...);
    return unique_ptr<T>(ptr);
    }

    ......一切都很好。一个特殊的模板参数推导规则有助于区分左值和右值参数,并且 std::forward 允许您创建与实际参数具有相同“值”的表达式。所以,如果你写这样的东西:
    string foo();

    int main() {
    auto ups = make_unique<string>(foo());
    }

    foo 返回的字符串会自动移动到堆中。

    So if lvalues did bind to rvalues via an implicit copy, then you could write just one overload like create_object(legacy_string &&, legacy_vector &&, legacy_string &&) and it would more or less work like providing all the combinations of rvalue/lvalue reference overloads...



    嗯,这几乎等同于按值获取参数的函数。不开玩笑。

    Is this a significant enough improvement that it would be worth making e.g. an experimental patch for GCC?



    没有任何改善。

    关于C++0x 右值引用 - 左值-右值绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2749263/

    32 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com