【问题标题】:Support for `auto&&` for MSVC++2017支持 MSVC++2017 的`auto&&`
【发布时间】:2017-06-14 17:32:00
【问题描述】:

this answer 他们建议使用以下代码:

#include <iostream>

template <typename F>
class Finally {
    F f;
public:
    template <typename Func>
    Finally(Func&& func) : f(std::forward<Func>(func)) {}
    ~Finally() { f(); }

    Finally(const Finally&) = delete;
    Finally(Finally&&) = delete;
    Finally& operator =(const Finally&) = delete;
    Finally& operator =(Finally&&) = delete;
};

template <typename F>
Finally<F> make_finally(F&& f)
{
    return Finally<F>{ std::forward<F>(f) }; // This doesn't compile
    //This compiles: return { std::forward<F>(f) };
}


int main()
{
    auto&& doFinally = make_finally([&] { std::cout<<", world!\n"; });
    std::cout << "Hello";
}

作者链接到一个使用 Clang++/G++ 编译的演示。但是,此代码在 MSVC++2017 中无法为我编译。

错误信息是:

source_file.cpp(20): error C2280: 'Finally<main::<lambda_9000fb389e10855198e7a01ce16ffa3d>>::Finally(Finally<main::<lambda_9000fb389e10855198e7a01ce16ffa3d>> &&)': attempting to reference a deleted function
source_file.cpp(12): note: see declaration of 'Finally<main::<lambda_9000fb389e10855198e7a01ce16ffa3d>>::Finally'
source_file.cpp(26): note: see reference to function template instantiation 'Finally<main::<lambda_9000fb389e10855198e7a01ce16ffa3d>> make_finally<main::<lambda_9000fb389e10855198e7a01ce16ffa3d>>(F &&)' being compiled
        with
        [
            F=main::<lambda_9000fb389e10855198e7a01ce16ffa3d>
        ]

那么return { std::forward&lt;F&gt;(f) };return Finally&lt;F&gt;{ std::forward&lt;F&gt;(f) }; 有什么区别,一个编译,另一个不编译?

Demo

【问题讨论】:

  • auto&amp;&amp; 应该被 MSVC 支持。
  • Finally&lt;F&gt; make_finally(F&amp;&amp; f) 不应该是Finally&lt;std::decay_t&lt;F&gt;&gt; make_finally(F&amp;&amp; f)吗?
  • 为什么要用 Clang 编译? make_finally 的返回想要调用 Finally 的(已删除)移动构造函数。如果事件被优化,它应该仍然需要可用。
  • @1201ProgramAlarm 如果make_finally 是 RVO 的候选者,由于 C++17 保证 RVO,你不需要移动构造函数 IIRC
  • 人!这里没有移动。 make_finally() 直接在返回对象中创建一个Finally&lt;F&gt;,然后绑定到引用doFinally。即使没有新的值类别,即使有-fno-elide-constructors,也不会发生任何移动。

标签: c++ c++14 visual-studio-2017 c++17 auto


【解决方案1】:

那么return { std::forward&lt;F&gt;(f) };return Finally&lt;F&gt;{ std::forward&lt;F&gt;(f) };有什么区别

前者将返回对象初始化到位。所以当你有:

X foo() { return {a, b, c}; }
auto&& res = foo();

这将在适当位置构造一个X,然后将res 绑定到它。根本没有任何动静。由于 RVO,这不是一个可以省略的动作,也不是由于 C++17 中带有新值类别的保证省略而可以省略的一个动作。在任何时候,我们甚至都没有考虑移动任何东西。只有一个 X(即使在 C++11 中,即使使用 -fno-elide-constructors,因为没有构造函数调用 elide)。

相比之下,这个:

X bar() { return X{a, b, c}; }
auto&& res2 = bar();

在 C++17 之前,会创建一个临时的 X,然后将其移动到 bar() 的返回中。这将是 RVO 的候选者,并且移动肯定会被忽略,但为了避免移动,移动必须实际上可以开始。在我们的例子中,X 的移动构造函数被删除了,所以这段代码格式不正确。这导致了 Richard Smith 在P0135R0 中的评论最能描述的情况:

// error, can't perform the move you didn't want,
// even though compiler would not actually call it

C++17 之后,没有临时的了。我们有一个X 类型的prvalue,我们用它来初始化bar() 的返回对象,所以我们最终只是简单地从prvalue 的初始化程序初始化返回对象。该行为等同于上面的foo() 示例。不会有任何动作。

【讨论】:

  • 我不会说“直接初始化”,太容易混淆了。
  • @T.C.到处都删除了直接这个词。很难仔细挑选在不同上下文中没有不同含义的词。
  • "根本没有任何移动。由于 RVO 不会省略移动,也不会因为 C++17 中的新值类别保证省略而省略移动。 ”。这非常令人困惑,因为您在“根本不移动”和“在 C++17 中保证会被忽略的移动”之间有所不同。因此,负 1
  • @JohannesSchaub-litb 我猜不能取悦所有人。我试图确定缺少移动 不是 的原因是由于 C++17。实际论文被称为“保证复制省略”也无济于事
猜你喜欢
  • 1970-01-01
  • 2018-09-25
  • 1970-01-01
  • 2017-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-15
相关资源
最近更新 更多