在推导函数参数时,是否可以考虑隐式用户定义转换 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
时,它才有效.
如果你想让它起作用,你有几个不同的选择(按照我个人的偏好顺序):
您可以使 Test
成为一个更广泛的函数模板,接受它实际可以使用的任何东西,而不是仅专门 std::array<T, N>
。鉴于您说“特别是如果 class 已经提供了必要的属性(value_type 等)”,这表明您想要的 Test
并不是特定的 std::array
.所以你应该弄清楚那些实际属性是什么,然后编写一个 appropriately-constrained 函数模板。例如,ranges::contiguous_range
是否足够,或者您是否真的需要 compile-time 大小?你甚至需要连续性吗?等等
您可以明确地将 Array_t
转换为 call-side 上的 std::array
。如果你给转换函数一个名字,这会更好,所以你可以写类似 obj.to_array()
而不是 static_cast<std::array<T, N>>(obj)
的东西(这既要输入更长的时间,又要让参数正确更烦人)。
您可以显式地向 Test
提供必要的模板参数,例如 Test<uint8_t, 32>(obj)
- 这避免了需要推断 T
和 N
,现在您只有一个接受 std::array<uint8_t, 32>
的普通函数,因此转换工作正常。请注意,您必须提供两个,Test<uint8_t>(obj)
是不够的。
您可以使 Array_t<T, N>
继承自 std::array<T, N>
,而不仅仅是转换为它。马上就可以了。
考虑以下示例,它试图将 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
时,它才有效.
如果你想让它起作用,你有几个不同的选择(按照我个人的偏好顺序):
您可以使
Test
成为一个更广泛的函数模板,接受它实际可以使用的任何东西,而不是仅专门std::array<T, N>
。鉴于您说“特别是如果 class 已经提供了必要的属性(value_type 等)”,这表明您想要的Test
并不是特定的std::array
.所以你应该弄清楚那些实际属性是什么,然后编写一个 appropriately-constrained 函数模板。例如,ranges::contiguous_range
是否足够,或者您是否真的需要 compile-time 大小?你甚至需要连续性吗?等等您可以明确地将
Array_t
转换为 call-side 上的std::array
。如果你给转换函数一个名字,这会更好,所以你可以写类似obj.to_array()
而不是static_cast<std::array<T, N>>(obj)
的东西(这既要输入更长的时间,又要让参数正确更烦人)。您可以显式地向
Test
提供必要的模板参数,例如Test<uint8_t, 32>(obj)
- 这避免了需要推断T
和N
,现在您只有一个接受std::array<uint8_t, 32>
的普通函数,因此转换工作正常。请注意,您必须提供两个,Test<uint8_t>(obj)
是不够的。您可以使
Array_t<T, N>
继承自std::array<T, N>
,而不仅仅是转换为它。马上就可以了。