construction helper make_XYZ allowing RVO and type deduction even if XZY has noncopy constraint

萝らか妹 提交于 2019-12-17 18:29:40

问题


UPDATE1: C++17 added type deduction for constructors - which does not imply that the free function is an inferior solution.

UPDATE2: C++17 added guaranteed copy elision (the copy does not even take place conceptually). So with C++17 my code actually works and with optimal performance. But Martinho's code using brace initialisation for the return value is still the cleaner solution I believe. But checkout this answer from Barry and the comment from T.C.

OLD POST: Type deduction does not work for constructors (at least until and including C++11). The common solution is to rely on RVO (Return Value Optimisation) and write a make_XYZ template function that forwards its parameters to the constructor. An example is std::make_tuple.

Any template acrobat who knows a workaround to make this work when nocopy policy is in the way? A valid solution must still allow RVO to happen.

Additionally, will the requirement for any make_XYZ disappear with C++14?

#include <iostream>

template <typename T>
struct XYZ
{
    // remove following two lines to make the code compile
    XYZ (XYZ const & rhs) = delete; 
    XYZ (XYZ && rhs) = delete; 
    T i;
    XYZ (T i):i(i)
    {
    }
};

template <typename T>
XYZ<T> make_XYZ (T && i)
{
    return XYZ<T>(std::forward<T>(i));
}

int main ()
{
    auto x = make_XYZ(1);
    std::cout << x.i << std::endl;
}

回答1:


If there is a non-explicit constructor, it is indeed possible to return a non-copiable and non-movable type by value. See live example: http://coliru.stacked-crooked.com/a/89ef9d3115924558.

template <typename T>
XYZ<T> make_XYZ (T && i)
{
    return { std::forward<T>(i) };
}

The tricky bit here is that { ... } is not constructing a temporary and moving it to the return value. It is directly initialising the return value. There is no copy nor move, and that is irrelevant of whether any optimisation applies (it wouldn't compile if it required an optimisation to work).

However, since the type is not copyable nor movable, you will not be able to store it in a local variable by value. You can, however, use the old temporary lifetime extension trick to hold it:

auto&& x = make_XYZ(1);



回答2:


RVO is only ever an optimisation; the copy/move has to be available for use when returning an object (temporary or named) from a function.

I'd suggest using the make_XYZ only in a nonevaluated context, using decltype:

#include <utility>

struct noncopy {
    noncopy() {}
    noncopy(noncopy &&) = delete;
    noncopy(const noncopy &) = delete;
};

template<class T1, class T2>
struct noncopy_pair: public std::pair<T1, T2>, private noncopy {
    using std::pair<T1, T2>::pair;
};

template<class T1, class T2>
noncopy_pair<T1, T2> make_noncopy_pair(T1 &&t, T2 &&u);

int main() {
    auto &&x = decltype(make_noncopy_pair(1, 'c'))(1, 'c');
}

Unfortunately you have to repeat your arguments, but you can use a macro to work around this (and the macro is at least guaranteed safe, as no argument is evaluated more than once).



来源:https://stackoverflow.com/questions/19427196/construction-helper-make-xyz-allowing-rvo-and-type-deduction-even-if-xzy-has-non

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!