I have an external library which I can not modify. The library declares a template function that for some reason returns const
non-reference object:
template<class C>
const C foo();
I have another external library which I can not modify too. The library declares a class that is non-copyable and has a move constructor from a non-const object only:
struct bar {
bar();
bar(const bar&)=delete;
bar(bar&&);
};
Now I need to use foo<bar>
. A simple usage:
bar buz() {
return foo<bar>();
}
fails with
main.cpp: In function 'bar buz()': main.cpp:13:21: error: use of deleted function 'bar::bar(const bar&)' return foo<bar>(); ^ main.cpp:8:5: note: declared here bar(const bar&)=delete; ^~~
which makes sense, and no simple workaround make the code compile.
However, if I add some more complex workaround:
bar buz() {
return const_cast<bar&&>(std::move(foo<bar>()));
}
it compiles and the whole code work as expected (not only the simplified example above, but my real code too).
However, it is safe, or am I running into some undefined behavior? Is there any better workaround?
I have read and I understand questions about returning const
from functions (1, 2), and the common answer there seems to be that returning const
objects is discouraged in modern C++, but my question is not about it, but about how can I workaround the situation when an external library returns const
object.
As result of function call is by definition an R-Value itself, you do not need to apply
std::move
on it in return statement -const_cast<bar&&>(foo<bar>())
should be enough. That make code a little bit simpler to read.Still - there is no standard guarantee that this will always work for all
bar
types. Even more - this might in some cases lead to undefined behavior.(Imagine some very intrusive optimization, which completely eradicatesfoo
and makes its result an object in "static data" segment of memory - like iffoo
was aconstexpr
. Then calling moving constructor, which probably modifies its argument, might lead to access violation exception).All you can do is either switch to different library (or, if possible, ask library maintainer to fix API) or create some unit test and include it in your build process - as long as test passes, you should be OK (remember to use same optimization settings as in "production" build -
const_cast
is one of those things which strongly depends on compilation settings).Casting away the const will lead to undefined behavior if the move constructor for
bar
modifies anything. You can probably work around your issue like this without introducing undefined behavior:Having the
wrapped
member be mutable means that the member is non-const even though thewrapped_bar
object as a whole is const. Based on howfoo()
works, you may need to add members towrapped_bar
to make it work more like abar
.Technically speaking, you are exposing your program to undefined behavior. Since original object
C
(a temporary) was declaredconst
, const-casting and modifying it is illegal and against the standard. (I assume, move constructor does some modifications to the movee).That being said, it probably works in your environment and I do not see a better workaround.