Reputation: 8759
I have this piece of code which uses iostreams' xalloc
and pword
to store flags of various types as pointers. Since pword
exposes a void*&
, I have a simple wrapper to expose the stored type via an old C cast. But since its version 5, clang issues a warning that I don't understand well. I don't see how to avoid it. The simple test case looks as follows:
#include <iostream>
#include <string>
using namespace std::literals;
void*& pword()
{
static void* ptr;
return ptr;
}
const std::string*& sword()
{
return (const std::string*&) pword();
}
int main()
{
const auto s1 = "foo"s;
const auto s2 = "bar"s;
sword() = &s1;
std::cerr << *sword() << '\n';
sword() = &s2;
std::cerr << *sword() << '\n';
}
The warnings I get (which seem to forget the reference on void*
):
clang++-mp-devel -std=c++17 -Wcast-qual foo.cc && ./a.out
foo.cc:14:32: warning: cast from 'void *' to 'const std::string *&' (aka 'const basic_string<char,
char_traits<char>, allocator<char> > *&') must have all intermediate pointers const qualified
to be safe [-Wcast-qual]
return (const std::string*&) pword();
^
1 warning generated.
foo
bar
Also, I fail to see how to use C++ casts instead of the C almighty cast. Obvious attempts:
const std::string*& sword2()
{
return static_cast<const std::string*&>(pword());
}
end with errors I don't understand. The error seems to give a hint that the compiler might feel obliged to insert a copy somewhere, breaking the chain of references, but I don't see where and why.
foo.cc:19:10: error: non-const lvalue reference to type 'const std::string *' (aka 'const
basic_string<char, char_traits<char>, allocator<char> > *') cannot bind to a value of
unrelated type 'void *'
return static_cast<const std::string*&>(pword());
^ ~~~~~~~
Upvotes: 1
Views: 694
Reputation: 39818
This is equivalent to the famous fact that you can’t convert char**
to const char**
—because it would allow
const char cc='a';
const char *cp=&cc;
char *p;
const char **pp=&p; // error in question
*pp=cp; // i.e., p=&cc;
*p='z'; // oops
In this case, by describing a void*
as a const std::string*
, you can store pointers to constant strings there (like you do) and then use a static_cast
on the void*
to obtain a non-const
pointer to the string.
The answer is to use const_cast
to add const
and then (as HolyBlackCat said) reinterpret_cast
(and then be careful not to try modifying the strings!):
const std::string*& sword()
{
return reinterpret_cast<const std::string*&>(const_cast<const void*&>(pword()));
}
Aside from const-correctness, I do not remember definitively at the moment whether storing a T*
into a void*
variable via such a reinterpret_cast
is defined behavior; it is, however, something one expects to work in common implementations.
Upvotes: 1