Home > Mobile >  Is there any workaround for implicit user-defined conversion operator() to be considered when deduci
Is there any workaround for implicit user-defined conversion operator() to be considered when deduci

Time:02-14

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):

  1. 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.

  2. 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).

  3. 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.

  4. 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.

  • Related