In the following case I try to cast a pointer to a struct into a pointer to another struct (which has the same memory layout behind the scenes). I try to do this in a const correct way, however the compiler is complaining.
I looked at a similar issue but I need the constness to propagate and it doesn't work like described for me.
Demo
struct queue
{
// ...
};
typedef struct queue* queue_handle;
struct dummy_queue
{
// ...
};
struct queue_wrapper
{
auto get_queue() const -> queue_handle {
return reinterpret_cast<const queue_handle>(&d);
}
dummy_queue d;
};
int main()
{
queue_wrapper w;
w.get_queue();
}
Error:
<source>: In member function 'queue* queue_wrapper::get_queue() const':
<source>:17:16: error: 'reinterpret_cast' from type 'const dummy_queue*' to type 'queue_handle' {aka 'queue*'} casts away qualifiers
17 | return reinterpret_cast<const queue_handle>(&d);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I'm converting to a const pointer though (which gcc seems to misunderstand somehow). I do not need to change the pointer after returning it. How do I accomplish this conversion without errors?
const queue_handle applies the const to the pointer type itself, not the pointed to type as you intent to.
I would suggest adding a const_queue_handle alias to const queue* if you are already using aliases for the pointers (whether or not that is a good idea is a matter of opinion) and then you can use that as template argument to reinterpret_cast.
Also beware that even if the struct layout is identical, this is still undefined behavior territory. A compiler may assume that a pointer to one of the struct types won't alias one to the other and use that for optimization. You should probably at least compile everything with -fno-strict-aliasing when doing this.
Related
I get two errors C2440 and C2973 while compiling this code with visual studio 2019:
#include <iostream>
#include <array>
enum class First {
SOME_VALUE,
OTHER_VALUE
};
enum class Second {
PRIME,
BIS
};
template<First FIRST_VAL>
struct FirstData {
int i;
};
template<Second SECOND_VAL>
struct SecondData {
long l;
};
template<First FIRST_VAL,Second SECOND_VAL>
class Data :public SecondData<SECOND_VAL>, public FirstData<FIRST_VAL> {
};
template<First FIRST_VAL>
struct WithFirstData {
virtual const FirstData<FIRST_VAL >& getData() = 0;
virtual ~WithFirstData() = default;
};
template<Second SECOND_VAL>
struct WithSecondData {
virtual const SecondData<SECOND_VAL>& getData() = 0;
virtual ~WithSecondData() = default;
};
template<First FIRST_VAL, Second SECOND_VAL>
class WithData : public WithFirstData<FIRST_VAL>, public WithSecondData<SECOND_VAL> {
public:
const First First;
const Second Second;
private:
const Data<FIRST_VAL, SECOND_VAL> data;
public:
virtual const Data<FIRST_VAL, SECOND_VAL>& getData() {
return data;
}
template<typename ... DataArgs>
WithData(DataArgs &&... data) :First(FIRST_VAL), Second(SECOND_VAL), data(std::forward<DataArgs>(data)...) { }
virtual ~WithData() {};
};
class ConcreteWithData : public WithData<First::SOME_VALUE, Second::BIS> {
public:
ConcreteWithData() : WithData() {
}
};
std::ostream& operator<<(std::ostream& ostream, const ConcreteWithData& o) {
ostream << static_cast<int>(o.First);
return ostream;
}
int main() {
ConcreteWithData ev;
std::cout << "Hello World!\n" << ev;
}
Except from error messages:
1>C:\[...]\Test.cpp(55): error C2440: 'specialization': cannot convert from 'int' to 'First'
1>C:\[...]\Test.cpp(55): message : Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)
1>C:\[...]\Test.cpp(55): error C2440: 'specialization': cannot convert from 'int' to 'Second'
1>C:\[...]\Test.cpp(55): message : Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)
1>C:\[...]\Test.cpp(55): error C2973: 'Event': invalid template argument 'int'
1>C:\[...]\Test.cpp(44): message : see declaration of 'Event'
1>C:\[...]\Test.cpp(44): error C2440: 'specialization': cannot convert from 'int' to 'First'
1>C:\[...]\Test.cpp(44,36): message : Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)
1>C:\[...]\Test.cpp(44): error C2440: 'specialization': cannot convert from 'int' to 'Second'
1>C:\[...]\Test.cpp(44,70): message : Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)
1>C:\[...]\Test.cpp(44,1): error C2973: 'WithSecondData': invalid template argument 'int'
1>C:\[...]\Test.cpp(38): message : see declaration of 'WithSecondData'
Funny thing is that this compiles no-problem under gcc (g++.exe (Rev6, Built by MSYS2 project) 10.2.0).
Who is at fault here? me for doing things that shouldn't be understood by compiler; or vs compiler for not understanding enums properly; or gcc for allowing something it should not?
EDIT 2:
This compiles if I refactor it to unscoped enums and ints as template arguments, so a work-around is there, but I would love to be able to use scoped enums. And, more importantly, I would love to understand what went wrong in a first place.
EDIT 1:
The original code was a bit bigger I boiled it down a bit, but basically what I wanted to achieve was to have specializations with diferent data for FirstData and SecondData based on diferent enum values. So then I could use references to WithFirstData<SOME_VALUE> WithSecondData<PRIME> when refering to diferent concrete implementations and be sure that those implementations have the data i am looking for.
tl;dr: See the solution at https://godbolt.org/z/ez1K43z8T.
I know that this is a pretty old question at this point, but since I recently came across the same issue in one of my projects, I wanted to discuss my findings and a potential workaround for others who may be experiencing similar problems.
The first thing to note is the fact that, as was mentioned in the comments by another user, this appears to be a bug with the MSVC. I cannot elaborate on why it happens, since I don't work on the compiler myself, but from my own findings, it seems to be internally converting scoped enums to their underlying types.
Let's take a closer look at one of the compiler's error messages:
C:\[...]\Test.cpp(55): error C2440: 'specialization': cannot convert from 'int' to 'First'
C:\[...]\Test.cpp(55): message : Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)
For some inexplicable reason, the MSVC is attempting to convert an int value to an enumeration value from the scoped enum First. Why is it doing it from an int variable, though? That's because int is the default underlying type of a scoped enum:
static_assert(std::is_same_v<std::to_underlying_t<First>, int>); // The assertion succeeds!
So, as a workaround, here is a C++20 concept which compares two types, with at least one of them being an enumeration, and checks to see if the MSVC would consider them "equivalent" for the sake of working around this issue:
template <typename LHSType, typename RHSType>
concept IsEnumEquivalent = (std::is_enum_v<LHSType> && !std::is_enum_v<RHSType> && std::is_same_v<std::underlying_type_t<LHSType>, RHSType>) ||
(std::is_enum_v<RHSType> && !std::is_enum_v<LHSType> && std::is_same_v<std::underlying_type_t<RHSType>, LHSType>) ||
(std::is_enum_v<LHSType> && std::is_enum_v<RHSType> && std::is_same_v<LHSType, RHSType>);
Then, you could define your FirstData and SecondData structs like so:
template <IsEnumEquivalent<First> FIRST_TYPE, FIRST_TYPE FIRST_VAL>
struct FirstData
{
static constexpr int i = 0;
};
template <IsEnumEquivalent<Second> SECOND_TYPE, SECOND_TYPE SECOND_VAL>
struct SecondData
{
static constexpr long l = 0;
};
along with any explicit template specializations:
template <>
struct FirstData<First, First::SOME_VALUE>
{
static constexpr int i = 5;
};
// Continue defining explicit specializations...
That's about it for working around the MSVC bug. However, since you mentioned a more specific problem, I'll discuss how this solution could be used to solve it. As the OP mentioned, the goal was for concrete implementations to have access to specific data, presumably defined at compile time. (If the data should be modifiable at runtime, then template metaprogramming probably isn't the answer you were looking for in the first place.)
An interesting thing to note is that our concept IsEnumEquivalent will accept int values, should the user wish to provide them. Considering that the whole point of this was to make use of the type safety of scoped enums, it would be nice to prevent this. One way to do this is as follows:
namespace IMPL
{
template <IsEnumEquivalent<First> FIRST_TYPE, FIRST_TYPE FIRST_VAL, IsEnumEquivalent<Second> SECOND_TYPE, SECOND_TYPE SECOND_VAL>
struct WithDataIMPL
{
static constexpr FirstData<FIRST_TYPE, FIRST_VAL> FirstDataElement{};
static constexpr SecondData<SECOND_TYPE, SECOND_VAL> SecondDataElement{};
};
}
template <First FIRST_VAL, Second SECOND_VAL>
using WithData = IMPL::WithDataIMPL<First, FIRST_VAL, Second, SECOND_VAL>;
Essentially, we are defining WithData as a type alias for an implementation structure contained within the IMPL namespace. This is my personal convention for organizing implementation details; most other projects I find call this the details namespace or something similar. Regardless of the convention you adopt, the idea is that implementation details are hidden from API users.
If you are using C++20 modules, then you can actually prevent other modules from ever seeing these implementation details by choosing not to export them! However, if you are still using traditional header files, then you will just have to ignore whatever details inevitably leak into other source files.
Anyways, going back to the original problem, we can define a "concrete implementation" class as follows:
class ConcreteWithData : public WithData<First::SOME_VALUE, Second::BIS>
{
// Do whatever you want with ConcreteWithData here. However, if you don't
// plan on adding anything else to this class, then it may be better to just
// make it a type alias:
//
// using ConcreteWithData = WithData<First::SOME_VALUE, Second::BIS>;
};
Accessing its data is then done as follows:
std::ostream& operator<<(std::ostream& ostream, const ConcreteWithData& o)
{
ostream << o.FirstDataElement.i << '\n';
ostream << o.SecondDataElement.l << '\n';
return ostream;
}
Wow, this post ended up being a lot longer than I was expecting it to be. Hopefully, it ends up being helpful to others who are/were experiencing the same issue as the OP was. If you want more details, the following Godbolt link includes a more detailed description of this solution as it pertains to this particular problem: https://godbolt.org/z/ez1K43z8T.
I was trying to make my code less bloated when dealing with windows API by replacing two-liners not unlike
TEMP t{0,1,2}; // let's say it's struct TEMP {int a; int b; int c}
SomeVeryVerboseFunctionName(&t);
with one-liners
SomeVeryVerboseFunctionName(&TEMP{0,1,2});
but stumbled upon error:
expression must be an lvalue or function designator.
After many attempts I finally came up with code that does compile (MSVS 2013u4):
SomeVeryVerboseFunctionName(&(TEMP) TEMP{0,1,2});//explicit cast to the same type!
To better understand why the cast is needed I set up a simple test project:
#include <stdio.h>
struct A
{
int a;
int b;
A(int _a, int _b) : a(_a), b(_b) {};
};
struct B
{
int a;
int b;
};
template <typename T> void fn(T* in)
{
printf("a = %i, b = %i\n", in->a, in->b);
}
int main()
{
fn(&A{ 1, 2 }); //OK, no extra magick
/* fn(&B {3, 4}); //error: expression must be an lvalue or function designator */
fn(&(B)B{ 3, 4 }); //OK with explicit cast to B (but why?)
}
and found out that if some struct T has an explicit constructor (like A has in the above code), then it's possible to take address of brace-initialized temporary of type T and pass it to a function that takes a pointer T*, but if it doesn't have one (like B), then the said error arises and can only be overcome by explicit casting to type T.
So the question is: why does B require such strange casting and A doesn't ?
Update
Now that it's clear that treating rvalue as lvalue is an extension/feature/bug in MSVS, does anyone care to pretend it's actually a feature (enough used for MS to maintain it since 2010) and elaborate on why temporaries of A and B need to be passed in different ways in order to satisfy the compiler? It must have something to do with A's constructor and B's lack thereof...
What you are doing is actually illegal in C++.
Clang 3.5 complains:
23 : error: taking the address of a temporary object of type 'A' [-Waddress-of-temporary]
fn(&A {1, 2}); //OK, no extra magick
^~~~~~~~~
25 : error: taking the address of a temporary object of type 'B' [-Waddress-of-temporary]
fn(&(B) B {3, 4}); //OK with explicit cast to B (but why?)
^~~~~~~~~~~~~
All operands of & must be lvalues, not temporaries. The fact that MSVC accepts these constructs is a bug. According to the link pointed out by Shafik above, it seems that MSVC erroneously creates lvalues for these.
template<class T>
T& as_lvalue(T&& t){return t;}
// optional, blocks you being able to call as_lvalue on an lvalue:
template<class T>
void as_lvalue(T&)=delete;
will solve your problem using legal C++.
SomeVeryVerboseFunctionName(&as_lvalue(TEMP{0,1,2}));
in a sense, as_lvalue is an inverse-move. You could call it unmove, but that would get confusing.
Taking the address of an rvalue is illegal in C++. The above turns the rvalue into an lvalue, at which point taking the address becomes legal.
The reason why taking an address of an rvalue is illegal is that such data is meant to be discarded. The pointer will only remain valid until the end of the current line (barring the rvalue being created via a cast of an lvalue). Such pointers only have corner-case usefulness. However, in the case of windows APIs, many such APIs take pointers to data structures for C-style versioning purposes.
To that end, these might be safer:
template<class T>
T const& as_lvalue(T&& t){return t;}
template<class T>
T& as_mutable_lvalue(T&& t){return t;}
// optional, blocks you being able to call as_lvalue on an lvalue:
template<class T>
void as_lvalue(T&)=delete;
template<class T>
void as_mutable_lvalue(T&)=delete;
because the more likely to be correct one returns a const reference to data (why are you modifying a temporary?), and the longer one (hence less likely to be used) returns the non-const version.
MSVC has an old "bug"/"feature" where it treats many things as lvalues when it should not, including the result of casts. Use /Za to disable that extension. This can cause otherwise working code to fail to compile. It may even cause working code to fail to work and still compile: I have not proved the contrary.
I have a function
void foo(int *bar)
{}
Visual Studio 2012 will now happily and without warnings compile if I call foo like this:
int main()
{
foo(false);
return 0;
}
If I however change foo(false) to foo(true) than I get an error:
1>main.cpp(132): error C2664: 'foo' : cannot convert parameter 1 from 'bool' to 'int *'
1> Conversion from integral type to pointer type requires reinterpret_cast, C-style cast or function-style cast
Any idea how I can make sure that I get an error when passing false, too? My only idea is to add a second private function "void foo(bool bar)". Then I indeed get the desired error stating that foo(bool bar) is private. But this only works when foo is called from outside the class and it clutters my interface.
First, as to why it accepts false but not true: The literal false will be interpreted as the integral 0 and a literal 0 will convert to a null pointer of any type. true on the other hand cannot be converted to a pointer type as it converts to an integral 1.
As to how to solve your problem: Your approach with an extra bool overload will work fine for both public and non-public callers as long as you don't implement the private overload. In that case public callers will get a compile error and member callers will get a linker error.
However, you may wish to consider additional things as well.
Does your function need to accept null pointers? If not, change it to accept by reference instead and the problem just goes away.
If your function really does need to accept null pointers then I would suggest in this case to just trust your users to call it correctly instead of cluttering your interface trying to prevent every possible misuse.
In C++11, you may write:
void foo(bool) = delete; // cannot be use by anybody (class itself included).
As vs2012 doesn't support = delete,
You may try this hack using SFINAE:
template <typename T>
typename std::enable_if<std::is_same<T, int>::value, void>::type
foo(T* t);
With the old C++98 way 'private without implementation':
private: // Fail to compile from outside
void foo(bool); // No implementation -> fail to link time if internally used.
You can wait for C++17, when Concepts can be used anywhere auto is in C++11, and write your signature as:
void foo( IsActually<int*>::template test x ) { }
... which actually looks pretty bad, but they may clean up the syntax by then.
I get a real kick out of exploring the unusual corners of C++. Having learned about the real types of functions rather than function pointers from this question, I tried messing around with function typing and came up with this bizarre case:
typedef int Func(int);
int Foo(int x) { return 1; }
int main()
{
const Func*& f = &Foo;
return 0;
}
Since &Foo is an rvalue of type Func*, I figured that I should be able to put it in a const reference, but I get this error from g++ 4.6:
funcTypes.cpp: In function ‘int main()’:
funcTypes.cpp:7:23: error: invalid initialization of non-const reference of type ‘int (*&)(int)’ from an rvalue of type ‘int (*)(int)’
But f is const! It has become apparent to me that the application of const to a function (or reference/reference to pointer etc.) is simply ignored; this code compiles just fine:
template <typename A, typename B>
struct SameType;
template <typename A>
struct SameType<A, A> { };
typedef int Func(int);
int main()
{
SameType<const Func, Func>();
return 0;
}
I'm guessing this is how boost pulls off their is_function type trait, but my question is - why does C++ allow this by ignoring it instead of forbidding it?
EDIT: I realise now that in the first example f is non-const and that const FuncPtr& f = &Foo does work. However, that was just background, the real question is the one above.
But f is const!
No, it's not. You're confusing
const Func*& f = &Foo;
with
Func* const& f = &Foo;
The former is a non-const ref to a const pointer. The latter is a const ref to a non-const pointer.
That's why I always write the const-ness before the */& rather than before the type. I would always write the first case as
Func const*& f = &Foo;
and then read right to left: reference to a pointer to a const Func.
In c++03 it was not ignored, but illformed (and was an sfinae case). I guess they changed that in c++11 because then you can simply have function parameters be const F& and can pass to it rvalue function objects aswell as normal functions.
See this DR which made the change http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#295
&Foo is a pointer. In general, I would suggest avoiding references to pointers (const or no). At least, not unless you know what you're doing.
So you should have:
const Func *f = &Foo;
Or really, you can ditch the const entirely:
Func *f = &Foo;
why does C++ allow this by ignoring it instead of forbidding it?
Because you're talking about two different things.
In C++, there is a difference between a function type and a function pointer. Foo is a function type, specifically int(int). &Foo is a function pointer, of type int(*)(int). A function type degrades into a function pointer, where necessary (much like array types degrade into pointers). But they are distinct (just like arrays).
So your two cases are not the same. Your first case is dealing with a function pointer, and your second case is dealing with a function type (which is what the template argument is deduced as).
As for why function types swallow the const, that's because the values of function types are already implicitly constant. You can't change them. The only operation you can perform on a function type is () (or conversion to function pointer). So a const T is equivalent to T if T is a function type. Visual Studio 2010 actually gives a warning about that.
The following compiles fine:
typedef int Func(int);
int Foo(int x) { return 1; }
int main()
{
Func* const& f = &Foo; //ok
return 0;
}
Be aware that const statements are evaluated along with pointers and references from right to left. The last const to the very left you wrote translates to last possible position right of a Name (C++ FAQ on const placement). Hence
const Func*& f
Is translated by the compiler to
Func const*& f
Hence you get a reference to a constant pointer which is not what you want. Besides I would not use references to function pointer if you do not really have to.
No, f is not const. First of all, it is a reference to some mutable type (that mutable type happens to be a const pointer, i.e. a mutable pointer to something that you promise not to change through that particular pointer). However, with &Foo you are creating a temporary (of type Func*) and you cannot assign a temporary to a mutable reference. You can only assign it to a const reference. And that is precisely what the error message is telling you.
Sometimes the error messages can be a bit cryptic.
I put together an example on ideone to illustrate the types printed by gcc for a variety of things:
#include <iostream>
typedef int(Func)(int);
typedef Func* FuncPtr;
typedef FuncPtr& FuncPtrRef;
typedef FuncPtr const& FuncPtrConstRef;
template <typename T> void DisplayType() { T::foo(); }
int main() {
DisplayType<Func>();
DisplayType<FuncPtr>();
DisplayType<FuncPtrRef>();
DisplayType<FuncPtrConstRef>();
}
The compilation errors give:
Func is of type int ()(int) (not a valid type, should now be fixed in gcc)
FuncPtr is of type int (*)(int)
FuncPtrRef is of type int (*&)(int)
FuncPtrConstRef is of type int (* const&)(int)
In your error message you have int (*&)(int), which is a reference to a non-const pointer to a function type.
You are not allowed to bind an rvalue to a non-const reference.
Note: this has thus nothing to do with const being swallowed, as smparkes correctly diagnosed
Consider the following code:
template <typename T>
class B
{
};
template <typename T>
B<T> f(T& t)
{
return B<T>();
}
class A
{
class C {};
C c;
public:
A() {}
decltype(f(c)) get_c() const { return f(c); }
};
int main()
{
A a;
a.get_c();
}
When I try to compile this, I get the error:
test.cpp: In member function 'B<A::C> A::get_c() const':
test.cpp:31:46: error: conversion from 'B<const A::C>' to non-scalar type 'B<A::C>' requested
It seems that in the decltype, the compiler doesn't know that this is a const member function and therefore c is of type const C, and as a result incorrectly deduces the type of f(c) to be B<C> rather than B<const C> which is what it really is.
Am I doing something incorrectly, or is this a compiler bug? I use gcc 4.6, but 4.4 and 4.5 exhibit the same behaviour.
The compiler operates correctly according to the current C++0x WP. See this issue report, which is currently being worked on.
Possibly the final C++0x Standard won't change the meaning of your decltype application in the return type before the function name. You would need to move it to after the parameter list using -> decltype(f(c)), which hopefully will do The Right thing in final C++0x.
No, decltype is not supposed to take into account whether the function is const or not, because it can't. The same could have been written differently:
typedef decltype(f(c)) return_type;
return_type get_c() const { return f(c); }
Correction: decltype(f(c)) shouldn't even compile, because c is not static.
f needs to take an rvalue reference, not an lvalue reference.
I don't think you're allowed to use decltype on anything you wouldn't normally be able to call. I haven't been able to find anything in the standard that would allow you to access c, even within a decltype expression, outside of anywhere you could use c. Since you don't have a this pointer at the point you're trying to do your thing, I don't think you can do what you're trying to do. Doing so doesn't work in MSVC 2010 at least, and it has nothing to do with const.
I considered using declval to get one but you can't access A&&.c because A is an incomplete type at that point. I can't see anyway to do what you're trying to do other than something like so:
decltype(f(declval<C const>())) get_c() const { ... }