Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

const_cast and std::move to remove constness from non-reference

Tags:

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.

like image 604
Petr Avatar asked May 24 '16 15:05

Petr


Video Answer


2 Answers

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:

struct wrapped_bar {
    mutable bar wrapped;
};

bar buz()
{
    return foo<wrapped_bar>().wrapped;
}

Having the wrapped member be mutable means that the member is non-const even though the wrapped_bar object as a whole is const. Based on how foo() works, you may need to add members to wrapped_bar to make it work more like a bar.

like image 68
Vaughn Cato Avatar answered Oct 07 '22 07:10

Vaughn Cato


Technically speaking, you are exposing your program to undefined behavior. Since original object C (a temporary) was declared const, 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.

like image 27
SergeyA Avatar answered Oct 07 '22 08:10

SergeyA