Reputation: 4744
I'd like to implement a function template that takes two arguments, a T* and a T, but where the second argument's type is determined by the first. Here's a minimal non-working example:
#include <cstddef>
#include <cstring>
#include <cstdint>
#include <type_traits>
#include <vector>
template<typename T> void
patch(T *dst, T src)
{
static_assert(std::is_standard_layout_v<T>);
std::byte *p = reinterpret_cast<std::byte *>(&src);
std::vector newval(p, p + sizeof(src));
// In the real code, memcpy happens later if a transaciton commits
std::memcpy(dst, newval.data(), newval.size());
}
int
main()
{
std::uint16_t u16;
patch(&u16, 0); // Fails to compile because 0 is int, not uint16_t
}
This code unfortunately fails to compile because the type T to patch cannot be inferred in patch(&u16, 0)
, because 0 is an int rather than a std::uint16_t
. Obviously I could cast the 0 or call patch<uint16_t>(...)
, but in an ideal world I wouldn't have to.
On the other hand, I can work around the problem if the second argument involves some kind of non-trivial type computation. For example, the code will compile if I declare the function as:
template<typename T> void
patch(T *dst, std::decay_t<T> src) {/*...*/}
When I originally asked the question, I implemented the following and believed it didn't work. I must have made a mistake, however, because as pointed out by the selected answer, it does in fact work:
template<typename T> struct sametype {
using type = T;
};
template<typename T> using sametype_t = typename sametype<T>::type;
template<typename T> void
patch(T *dst, sametype_t<T> src) {/*...*/}
My question is what's the minimal transformation one can apply to a template function argument to force its type to be inferred by the type of a different argument to the same function?
Upvotes: 3
Views: 219
Reputation: 51866
This is exactly the kind of scenario that std::type_identity_t<T>
is intended to resolve. This transformation creates what's called a non-deduced context, which you've requested for your second parameter of type T
. Unfortunately this type trait wasn't introduced until the C++20 standard.
You can continue to use the one you've implemented (which by the way, yes it does compile at least according to godbolt), or if you're using boost, there is a boost::type_identity_t<T>
available.
To answer your question directly though, I believe one of the most minimal transformations necessary in C++17 would be this:
template<typename T> void
patch(T *dst, std::enable_if_t<true, T> src)
Upvotes: 3
Reputation: 38440
Is this an acceptable solution?
template<typename T, typename U> void
patch(T *dst, U src)
{
T s = src;
static_assert(std::is_standard_layout_v<T>);
std::byte *p = reinterpret_cast<std::byte *>(&s);
std::vector newval(p, p + sizeof(T));
// In the real code, memcpy happens later if a transaciton commits
std::memcpy(dst, newval.data(), newval.size());
}
Upvotes: 1