0

I want a way of preserving if the type is a pointer, const, etc upon a cast. See the following.

template<typename type>
void test(type t) {
    std::cout << "t const? = " << std::is_const<decltype(t)>() << std::endl;
    int x = (int) t;
    std::cout << "x const? = " << std::is_const<decltype(x)>() << std::endl;
};

int main() {
    const int x = 0;
    int y = 0;

    test<const int>(x);
    test<int>(y);
}
>>> t const? = 1
>>> x const? = 0
>>> t const? = 0
>>> x const? = 0

How do I make it so that the function prints the following? In other words, how do I make it so that if the template argument is const, x is also const?

>>> t const? = 1
>>> x const? = 1
>>> t const? = 0
>>> x const? = 0

I would like to avoid having to do anything like the following.

// bad
if constexpr (std::is_const<decltype(t)>()) {
    // do const cast
} else {
    // do non const cast
}

The specific scenario I have is I have a const or non-const void pointer and want to cast it to a pointer with a type, whilst preserving the constness.

Edit: This is a poor example. You can use type x = (type) t. I wanted a solution using type traits because this isn't a valid solution for me.

Jaan
  • 330
  • 2
  • 9
  • 2
    As a general rule of thumb, if you ever feel the need to do a C-style cast (like e.g. `(int) t`) you should take that as a sign that you're probably doing something wrong. – Some programmer dude Jul 01 '22 at 16:52
  • 3
    On another note, if you have the type in `type`, why use `decltype(t)` instead of `type` directly? – Some programmer dude Jul 01 '22 at 16:53
  • This is a small example presented for readability. I am casting an integer because it's easier to read than my specific example. Also, I used `decltype` because I justifiably did it for x as well and wanted to keep the example consistent. – Jaan Jul 01 '22 at 16:53
  • Are you trying to copy constness from one type to another? – HolyBlackCat Jul 01 '22 at 16:58
  • That is correct. – Jaan Jul 01 '22 at 16:59
  • 1
    @Someprogrammerdude Eh. Only C-style casts to pointers/references are potentially unsafe. The only bad (or good, in my book) thing about C-style casts to non-pointers-non-references is their terseness. – HolyBlackCat Jul 01 '22 at 16:59
  • @Someprogrammerdude C-style casts are equivalent to functional casts and no one ever complains about `std::string("A")` (which is defined to be exactly the same as `(std::string) "A"`) and the like. C-style casts are only wrong when pointers or references might be involved. – HTNW Jul 01 '22 at 17:00
  • maybe I do not understand the question, but `type x = t;` produces desired output – 463035818_is_not_an_ai Jul 01 '22 at 17:10
  • This is a bad example. I'll fix it – Jaan Jul 01 '22 at 17:10
  • 1
    `using foo = std::conditional_t, const int, int>; foo x = (foo)t;` ? – Borgleader Jul 01 '22 at 17:13
  • Why nobody ask: why? What are you doing that you think you need such language functionality? – Marek R Jul 01 '22 at 17:22
  • @MarekR If you want to specific use case; I have an iterator of void pointers for c style polymorphism, and when I dereference the iterator I want to cast the void pointer to the actual type. My iterator takes an stl iterator wants to implement types like `pointer` and `reference`. If I wrote `using pointer = typename stl_iterator::pointer`, that would give me a void pointer. I want to cast it to the actual type. – Jaan Jul 01 '22 at 17:26
  • 1
    IMO you should ask question describing that actual problem (you trying describe in above comment). Currently question suffers from [XY problem](https://xyproblem.info/). I still do not see reason why you need transfer `const`. If template requites that `x` is const it should be stated `const`, if `x` do not changed you can add `const` for any version. – Marek R Jul 01 '22 at 17:30
  • @MarekR You are correct. I thought it was a good example until I missed the obvious solution. Thanks for your feedback. – Jaan Jul 01 '22 at 17:33
  • @HTNW The example `std::string("A")` is flawed, as it's not really a cast. It's object creation. `int(5.0)` would be a function-like cast, added to make it similar to object creation. And C-style cast tells the compiler to throw away all type-checks. It's telling the compiler "treat the value as the type I tell", doesn't matter if it makes sense or not. It's unfortunately a common (and bad) way to silence compiler errors and warnings about incompatible types, especially among beginners. Better learn good habits early. Such casts is a good way to shoot oneself in the feet. – Some programmer dude Jul 01 '22 at 18:24
  • Possible dupe of [Type trait for copying cv reference qualifiers](https://stackoverflow.com/questions/31171682/) – Remy Lebeau Jul 01 '22 at 19:02

1 Answers1

1

Your example isnt the best to illustrate the actual issue to cast a void*, possibly const, to some T* with same constness, because you can simply replace the line with type x = t; to get desired output.

However, you can use a type trait:

#include <iostream>
#include <type_traits>

template <typename From,typename To>
struct preserve_const {
    using type = std::remove_const<To>::type;
};

template <typename From,typename To>
struct preserve_const<const From,To> {
    using type = const To;
};

template <typename From,typename To>
using preserve_const_t = typename preserve_const<From,To>::type;

template<typename type>
void test(type t) {
    std::cout << "t const? = " << std::is_const<decltype(t)>() << std::endl;
    preserve_const_t<type,int> x = t;  
    std::cout << "x const? = " << std::is_const<decltype(x)>() << std::endl;
};

int main() {
    const int x = 0;
    int y = 0;

    test<const int>(x);
    test<int>(y);
}

Or as mentioned in comments and perhaps much simpler:

template <typename type,typename T>
using the_type = std::conditional_t< std::is_const_v<type>,const T,T>;
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • Great thanks. Apologies for the bad example. – Jaan Jul 01 '22 at 17:17
  • You might want to include a `using` alias for `preserve_const::type`, eg: `template using preserve_const_t = typename preserve_const::type;` Then `test()` can use it like this: `preserve_const_t x = t;` – Remy Lebeau Jul 01 '22 at 19:04