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.
CodePudding user response:
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 specificallystd::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 forTest
isn't specifically astd::array
. So you should figure out what those actual properties are and write an appropriately-constrained function template. For instance, does anyranges::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 astd::array
on the call-side. This works better if you give the conversion function a name, so you can write something likeobj.to_array()
rather thanstatic_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
, likeTest<uint8_t, 32>(obj)
- this avoids needing to deduceT
andN
, now you just have a normal function that takes astd::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 fromstd::array<T, N>
, instead of just converting to it. That works right off the bat.