Avoid exponential grow of const references and rvalue references in constructor

前端 未结 4 670
野趣味
野趣味 2020-12-04 17:43

I am coding some templated classes for a machine learning library, and I\'m facing this issue a lot of times. I\'m using mostly the policy pattern, where classes receive as

4条回答
  •  慢半拍i
    慢半拍i (楼主)
    2020-12-04 18:34

    This is exactly the use case for "pass by value and move" technique. Although slighly less efficient than lvalue/rvalue overloads, it not too bad (one extra move) and saves you the hassle.

    LinearClassifier(Loss loss, Optimizer optimizer) 
        : _loss(std::move(loss)), _optimizer(std::move(optimizer)) {}
    

    In the case of lvalue argument, there will be one copy and one move, in the case of rvalue argument, there will be two moves (provided that you classes Loss and Optimizer implement move constructors).

    Update: In general, perfect forwarding solution is more efficient. On the other hand, this solution avoids templated constructors which are not always desirable, because it will accept arguments of any type when not constrained with SFINAE and lead to hard errors inside the constructor if arguments are not compatible. In other words, unconstrained templated constructors are not SFINAE-friendly. See Barry's answer for a constrained template constructor which avoids this problem.

    Another potential problem of a templated constructor is the need to place it in a header file.

    Update 2: Herb Sutter talks about this problem in his CppCon 2014 talk "Back to the Basics" starting at 1:03:48. He discusses pass by value first, then overloading on rvalue-ref, then perfect forwarding at 1:15:22 including constraining. And finally he talks about constructors as the only good use case for passing by value at 1:25:50.

提交回复
热议问题