Reputation: 183
Consider the following example, which tries to pass a std::array
to a function. Naturally the "conversion" is not considered, but is there any work-around without having to be explicit? Especially if the class already provides the necessary properties (value_type etc.).
template <typename T, size_t N>
struct Array_t
{
using value_type = T;
using size_type = size_t;
std::array<T, N> Internal{};
constexpr operator auto() { return Internal; }
};
template <typename T, size_t N> constexpr bool Test(std::array<T, N> Input)
{
return Input.size() == 32;
}
constexpr std::array<uint8_t, 32> OK1 = Array_t<uint8_t, 32>();
constexpr auto OK2 = Test((std::array<uint8_t, 32>)Array_t<uint8_t, 32>{});
constexpr auto OK3 = Test(Array_t<uint8_t, 32>().Internal);
// could not deduce template argument for 'std::array<_Ty,_Size>'
// from 'Array_t<uint8_t,32>'
constexpr auto FAIL = Test(Array_t<uint8_t, 32>{});
To clarify, a workaround so that the Array_t struct can be passed directly to any function expecting a std::array. No casting, no helper/conversion function, just some way to make the compiler understand that the struct is convertible. Possibly in a similar manner to CTAD.
Upvotes: 0
Views: 265
Reputation: 303057
Template deduction never considers (user-defined) conversion. Given your:
template <typename T, size_t N>
constexpr bool Test(std::array<T, N> Input);
If you see, in code, Test(x)
, then that is only ever valid if x
is either specifically some kind of std::array
or inherits (publicly and unambiguously) from that.
If you want this to work, you have several different options of what to do (in my personal preference order):
You can make Test
a broader function template, accepting anything that it could actually use instead of only specifically std::array<T, N>
. Given that you said "Especially if the class already provides the necessary properties (value_type etc.)", this suggests that what you want for Test
isn't specifically a std::array
. So you should figure out what those actual properties are and write an appropriately-constrained function template. For instance, does any ranges::contiguous_range
suffice or do you actually need compile-time size? Do you even need contiguity? etc.
You can explicitly convert your Array_t
to a std::array
on the call-side. This works better if you give the conversion function a name, so you can write something like obj.to_array()
rather than static_cast<std::array<T, N>>(obj)
(which is both much longer to type and more annoying to get the parameters correct).
You can explicitly provide the necessary template arguments to Test
, like Test<uint8_t, 32>(obj)
- this avoids needing to deduce T
and N
, now you just have a normal function that takes a std::array<uint8_t, 32>
, so conversions work just fine. Note that you have to provide both, Test<uint8_t>(obj)
is insufficient.
You can make Array_t<T, N>
inherit from std::array<T, N>
, instead of just converting to it. That works right off the bat.
Upvotes: 5