支持MSVC ++ 2017的“auto&&”

时间:2017-06-14 17:32:01

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

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<F>(f) };return Finally<F>{ std::forward<F>(f) };之间有什么区别,但是另一个没有?

Demo

1 个答案:

答案 0 :(得分:4)

  

那么return { std::forward<F>(f) };return Finally<F>{ std::forward<F>(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的移动构造函数被删除,因此这段代码是错误的。这导致理查德史密斯在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()示例。不会采取任何行动。