-3

Here's a minimal example of what I'm trying to achieve

#include <array>

int main() {
    std::array<int, 5>* arr = new std::array<int, 5>{}; // no fill
    int** temp = new int*;

    for (const auto& e : *arr) {
        // some code that uses e...
        *temp = const_cast< std::remove_reference<decltype(e)>* >(&e); //error
        *temp = const_cast< int* >(&e); //ok
    }

    **temp = 22;
}

I need to access the type of e with decltype, and also to point to e, so I remove the reference from the decl type and declare it with a pointer.

But it doesn't work and treats std::remove_reference as a class invalid 'const_cast' from type 'const int*' to type 'std::remove_reference<const int&>*'

How can I fix it?

baronsec
  • 154
  • 1
  • 10
  • 5
    What's the point of casting `&e` to the exact type it already has? Besides, `e` is an `int*` pointer (to the first approximation, with some const qualifiers removed for clarity). `&e` is `int**`. `*temp` is an `int*`. You are trying to assign a double pointer to a single pointer - that ain't gonna work, const or no const. It's not clear what you are trying to achieve, but it's clear you've miscounted your stars somewhere along the way. – Igor Tandetnik May 27 '23 at 15:25
  • 3
    Even if this worked, you'd have problems because `temp` is uninitialized so accessing `*temp` is undefined behavior. Also, even aside from the `const` issue, `&e` is a pointer to a pointer to an `int`. `*temp` is just a pointer to an `int`. Your usage of pointers has made this code much more confusing. – Nathan Pierson May 27 '23 at 15:29
  • Btw. **there is no need to use `fill` just write** `std::array* arr = new std::array{}`. Misuse of cast spotted! – Jason May 27 '23 at 15:37
  • The typo is `std::remove_reference` -> `std::remove_reference_t`. Then you have a separate error of `std::remove_reference_t*` being `const int*`. – Artyer May 27 '23 at 17:35
  • Another side note, 9 out of 10 times a const cast means you're breaking some kind of design, and thus const_cast is usually a code smell. The only times I ever have needed it is when something is definitly const from C++ (api) view, but some 3rd party api forgot a const parameter. And in those cases it is sometimes safer to make copy then use a const cast (unless you can prove that api really doesn't change the value ever) – Pepijn Kramer May 27 '23 at 17:41
  • It looks like the original problem is a specific case of the more abstract [How to get the type of elements in a container?](https://stackoverflow.com/q/16503891) – JaMiT May 27 '23 at 20:48

2 Answers2

2

decltype(&e) gives you exactly the type of the expression &e with the value category translated to the matching reference-qualification.

So the cast const_cast<decltype(&e)>(&e) doesn't do anything. It casts &e which has type int* const* and value category prvalue to type int* const*, i.e. const_cast<decltype(&e)>(&e) is an expression that itself has type int* const* and value category prvalue and the result has the same value as &e.

If you want to remove the const, you want a cast to int**, not int* const*. You need to remove the const from the type, e.g. with std::remove_cvref_t you can remove reference and const qualification from e's declared type (which is what decltype(e) will return):

*temp = const_cast<std::remove_cvref_t<decltype(e)>*>(&e);

However, this is dangerous. Someone might change *arr to be an object that is non-modifiable and then the compiler won't give you any warning or error anymore, because the const_cast suppresses it. Then you'll have undefined behavior.

If you need the reference to be const most of the time, it is better to start with a non-const reference and then add a const one for the scope where it should be const:

for (auto& e_modifiable : *arr) {
    {
        const auto& e = e_modifiable;
        // some code that uses e...
    }
    // *temp was probably unintended. It has the wrong type and
    // temp is uninitialized. However `**temp = 22;` also is UB
    // because all elements of the array are null pointers.
    temp = &e_modifiable; // error
}

Also note other comments about your code under your question which are valid. All in all I suspect that you are using way too many pointers and new for what you are trying to do, although there isn't enough context to know what exactly that is.

user17732522
  • 53,019
  • 2
  • 56
  • 105
1

First things first, there is no need to use std::array::fill when you can use:

//------------------------------------------------vv-->use zero initialization
std::array<int*, 5>* arr = new std::array<int*, 5>{}; 
//no need to use fill here

Now coming to the error:

Why doesn't it work?

Because a) your use of the const_cast to remove the const is incorrect. Also you're casting &e to the type it already has. You might have omitted the cast altogether if this is what was needed.

More importantly, even if there was no low-level const, and we had just int** instead of int* const*, still an expression of type int** could not be assigned to an expression of type int* as there is no conversion possible between the two(both explicit or implicit).


Also note that temp is uninitialized so *temp is undefined behavior.

Jason
  • 36,170
  • 5
  • 26
  • 60