[libcamera-devel] [PATCH] py: Drop redundant std::move()
Tomi Valkeinen
tomi.valkeinen at ideasonboard.com
Fri Apr 28 17:27:29 CEST 2023
On 28/04/2023 18:10, Tomi Valkeinen wrote:
> On 28/04/2023 18:03, Tomi Valkeinen wrote:
>> On 28/04/2023 17:51, Laurent Pinchart wrote:
>>> Hi Tomi,
>>>
>>> On Wed, Apr 05, 2023 at 09:49:45AM +0300, Tomi Valkeinen wrote:
>>>> On 25/01/2023 11:42, Laurent Pinchart via libcamera-devel wrote:
>>>>> On Wed, Jan 25, 2023 at 12:09:40AM +0000, Kieran Bingham wrote:
>>>>>> Quoting Laurent Pinchart via libcamera-devel (2023-01-24 23:36:24)
>>>>>>> gcc-13 warns that the valueOrTuple() function has a redundant
>>>>>>> std::move() in a return statement:
>>>>>>>
>>>>>>> ../../src/py/libcamera/py_helpers.cpp: In instantiation of
>>>>>>> ‘pybind11::object valueOrTuple(const libcamera::ControlValue&)
>>>>>>> [with T = bool]’:
>>>>>>> ../../src/py/libcamera/py_helpers.cpp:38:28: required from here
>>>>>>> ../../src/py/libcamera/py_helpers.cpp:28:35: error: redundant
>>>>>>> move in return statement [-Werror=redundant-move]
>>>>>>> 28 | return std::move(t);
>>>>>>
>>>>>> ohhh - this may be just too pedantic for me. Explicitly stating
>>>>>> std::move(t) when the compiler knows it is a move may be redundant to
>>>>>> the compiler, but it's not redundant to the reader?!
>>>>>>
>>>>>> Doesn't this help make it clear that the t is being moved... in which
>>>>>> case it's helpful self documenting code?
>>>>>>
>>>>>> I'm normally all for warnings, but this one is annoying.
>>>>>>
>>>>>> https://developers.redhat.com/blog/2019/04/12/understanding-when-not-to-stdmove-in-c
>>>>>> states that this isn't a 'pessimizing' operation, it's just
>>>>>> redundant,
>>>>>> but it does make it clearer that a move is expected to occur?
>>>>>
>>>>> Adding more context, the function is implemented as
>>>>>
>>>>> if (cv.isArray()) {
>>>>> const T *v = reinterpret_cast<const T *>(cv.data().data());
>>>>> auto t = py::tuple(cv.numElements());
>>>>>
>>>>> for (size_t i = 0; i < cv.numElements(); ++i)
>>>>> t[i] = v[i];
>>>>>
>>>>> return std::move(t);
>>>>> }
>>>>>
>>>>> return py::cast(cv.get<T>());
>>>>>
>>>>> The type of 't' is py::tuple (replacing 'auto' with 'py::tuple' still
>>>>> produces the same warning), which inherits from py::object. We thus
>>>>> seem
>>>>> to be in the last case described by the above link:
>>>>>
>>>>> There are situations where returning std::move (expr) makes
>>>>> sense,
>>>>> however. The rules for the implicit move require that the
>>>>> selected
>>>>> constructor take an rvalue reference to the returned object's
>>>>> type.
>>>>> Sometimes that isn't the case. For example, when a function
>>>>> returns
>>>>> an object whose type is a class derived from the class type the
>>>>> function returns. In that case, overload resolution is
>>>>> performed a
>>>>> second time, this time treating the object as an lvalue:
>>>>>
>>>>> struct U { };
>>>>> struct T : U { };
>>>>>
>>>>> U f()
>>>>> {
>>>>> T t;
>>>>> return std::move (t);
>>>>> }
>>>>>
>>>>> g++-13 produces a warning when compiling that code:
>>>>>
>>>>> move.cpp: In function ‘U f()’:
>>>>> move.cpp:9:26: warning: redundant move in return statement
>>>>> [-Wredundant-move]
>>>>> 9 | return std::move (t);
>>>>> | ~~~~~~~~~~^~~
>>>>> move.cpp:9:26: note: remove ‘std::move’ call
>>>>>
>>>>> This may also be a false positive of gcc ?
>>>>
>>>> I don't have gcc 13, nor does godbolt.org, but other gcc nor clang
>>>> versions don't complain.
>>>>
>>>> With some testing on godbolt, with and without std::move the end result
>>>> is the same (with -O2) on the compilers I tested.
>>>>
>>>> So... I don't know. The text you pasted seems to suggest that
>>>> std::move() would be needed there, but I don't see a diff (then again,
>>>> my test code is just test code, not the actual py code we have). I'm
>>>> fine either way, but if gcc 13 is not much used yet, maybe we should
>>>> wait a bit?
>>>>
>>>> Also, a bit beside the point, I'm actually a bit surprised that
>>>>
>>>> U f()
>>>> {
>>>> return T();
>>>> }
>>>>
>>>> works without warnings (even if I add fields to U and T). It's silently
>>>> throwing away the T specific parts, only keeping the U parts.
>>>
>>> I tried this test code:
>>>
>>> --------
>>> #include <functional>
>>> #include <iostream>
>>>
>>> struct Base {
>>> Base()
>>> {
>>> std::cout << "Base::Base()" << std::endl;
>>> }
>>>
>>> Base(const Base &)
>>> {
>>> std::cout << "Base::Base(const Base &)" << std::endl;
>>> }
>>>
>>> Base(Base &&)
>>> {
>>> std::cout << "Base::Base(Base &&)" << std::endl;
>>> }
>>> };
>>>
>>> struct Derived : Base {
>>> };
>>>
>>> Base f()
>>> {
>>> Derived t;
>>> return std::move(t);
>>> }
>>>
>>> Base g()
>>> {
>>> Derived t;
>>> return t;
>>> }
>>>
>>> int main()
>>> {
>>> f();
>>> g();
>>>
>>> return 0;
>>> }
>>> --------
>>>
>>> $ g++-12 -W -Wall -o move2 move2.cpp && ./move2
>>> Base::Base()
>>> Base::Base(Base &&)
>>> Base::Base()
>>> Base::Base(const Base &)
>>>
>>> $ g++-13 -W -Wall -o move2 move2.cpp && ./move2
>>> move2.cpp: In function ‘Base f()’:
>>> move2.cpp:27:25: warning: redundant move in return statement
>>> [-Wredundant-move]
>>> 27 | return std::move(t);
>>> | ~~~~~~~~~^~~
>>> move2.cpp:27:25: note: remove ‘std::move’ call
>>> Base::Base()
>>> Base::Base(Base &&)
>>> Base::Base()
>>> Base::Base(Base &&)
>>>
>>> This is annoying. The move seems to be redundant indeed with g++-13, but
>>> dropping it results in the copy constructor being used with g++-12 and
>>> earlier.
>>>
>>
>> What about:
>>
>> Derived t;
>> Base& b = t;
>> return std::move(b);
>>
>
> Ah, nice, godbolt now has gcc 13.
>
> The above seems to compile fine on gcc 13, and doesn't seem to change
> the produced code on gcc 12 and gcc 13.
>
> Tomi
>
Or this also seems to do the trick:
return static_cast<Base&&>(std::move(t));
Tomi
More information about the libcamera-devel
mailing list