在推导函数参数时,是否可以考虑隐式用户定义转换 operator() 的解决方法?

Is there any workaround for implicit user-defined conversion operator() to be considered when deducing function arguments?

考虑以下示例,它试图将 std::array 传递给函数。自然不考虑“转换”,但是否有任何无需明确说明的解决方法?特别是如果 class 已经提供了必要的属性(value_type 等)。

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>{});

澄清一下,一种解决方法,使 Array_t 结构可以直接传递给任何需要 std::array 的函数。没有转换,没有 helper/conversion 函数,只是让编译器理解结构是可转换的某种方式。可能采用与 CTAD 类似的方式。

模板推导从不考虑 (user-defined) 转换。鉴于您的:

template <typename T, size_t N>
constexpr bool Test(std::array<T, N> Input);

如果您在代码中看到 Test(x),那么只有当 x 是某种特定的 std::array 或(公开且明确地)继承自 std::array 时,它才有效.

如果你想让它起作用,你有几个不同的选择(按照我个人的偏好顺序):

  1. 您可以使 Test 成为一个更广泛的函数模板,接受它实际可以使用的任何东西,而不是仅专门 std::array<T, N>。鉴于您说“特别是如果 class 已经提供了必要的属性(value_type 等)”,这表明您想要的 Test 并不是特定的 std::array.所以你应该弄清楚那些实际属性是什么,然后编写一个 appropriately-constrained 函数模板。例如,ranges::contiguous_range 是否足够,或者您是否真的需要 compile-time 大小?你甚至需要连续性吗?等等

  2. 您可以明确地将 Array_t 转换为 call-side 上的 std::array。如果你给转换函数一个名字,这会更好,所以你可以写类似 obj.to_array() 而不是 static_cast<std::array<T, N>>(obj) 的东西(这既要输入更长的时间,又要让参数正确更烦人)。

  3. 您可以显式地向 Test 提供必要的模板参数,例如 Test<uint8_t, 32>(obj) - 这避免了需要推断 TN,现在您只有一个接受 std::array<uint8_t, 32> 的普通函数,因此转换工作正常。请注意,您必须提供两个Test<uint8_t>(obj)是不够的。

  4. 您可以使 Array_t<T, N> 继承自 std::array<T, N>,而不仅仅是转换为它。马上就可以了。