问题描述
我想使用我的用户定义概念作为 std::span 的模板类型,但模板参数推导没有按我预期的那样工作。当我尝试将“std::array
of char
”传递给模板函数时;编译器显示错误“error: no matching function for call to 'print'”并在我用鼠标将鼠标悬停在模板定义上时警告我“注意:候选模板被忽略:无法匹配'span' 对 'array'"。
这里是概念定义和函数模板:
#include <concepts>
#include <span>
template <typename T>
concept OneByteData = sizeof(T) == 1;
template<OneByteData T>
void print(std::span<const T> container)
{
for(auto element : container)
{
//Do Some Work
}
}
int main()
{
std::array<char,6> arr = {1,2,3,4,5,6};
print(arr);
return 0;
}
int main()
{
std::array<char,6};
print<char>(arr);
return 0;
}
有没有办法在不专门化数组类型的情况下调用这个模板函数。我应该如何更改模板函数定义以按照我提到的方式调用函数 (print(arr)
)?
编辑:我希望能够利用 std::span 的好处,并且能够使用 std::array、std::vector 和普通 C 样式调用模板函数数组。
解决方法
一个可能的解决方案是接收(并推导出)一个泛型类型,然后检查它是否可以转换为 std::span
大小为 1 的 element_type
。
我是说
template <typename T>
concept OneByteData = sizeof(T) == 1;
template <typename T>
void print (T container)
requires OneByteData<typename decltype(std::span{container})::element_type>
{
std::span cnt {container};
for(auto element : cnt )
{
//Do Some Work
}
}
// extra print for C-style cases
template <typename T,std::size_t N>
void print (T(&arr)[N])
{ print(std::span{arr}); }
另一种可能的解决方案是 print()
,类似于您的原始解决方案,它接收 std::span
并进行概念检查,以及一些额外的 print()
(一个特定于 C 样式数组)将推导出的类型转换为 std::span
template <typename T>
concept OneByteData = sizeof(T) == 1;
template<OneByteData T,std::size_t N>
void print(std::span<T,N> container)
{
for(auto element : container)
{
//Do Some Work
}
}
template <typename T,std::size_t N>
void print (T(&arr)[N])
{ print(std::span{arr}); }
template <typename T>
void print (T container)
{ print(std::span{container}); }
,
From my understanding 这是您实际应该如何使用它:
// Second template parameter for size
template<OneByteData T,N> container) {
// Do something
return;
}
// Create a span from a plain array
print(std::span{arr});
如果你不希望你的用户自己做,你可以
-
为
print
编写一个重载来处理从std::array
到std::span
的转换,例如 (Wandbox)template<OneByteData T,std::size_t N> void print(std::array<T,N> const& container) { return print<T const>(std::span{container}); }
-
否则,您可以重写
print
的接口以获取任何容器,使用 {{1} 强制对底层元素类型进行约束} 或用户 max66 等概念提出并在内部将这个通用容器转换为std::enable_if
。 -
对于类,您可以编写一个模板推导指南,它决定应该使用哪个构造函数 (Wandbox)
std::span
编辑
对于像评论中讨论的那样的运算符,我实际上会使用模板化函数和模板化运算符的组合。函数 template <typename T,std::size_t N>
Print(std::array<T,N>) -> Print<T,N>;
使用泛型 append
来处理 std::span
数据类型,而模板运算符将允许的数据类型转换为 OneByteData
并调用该函数。 std::span
确保可以将数据结构转换为具有正确数据类型的 OneByteData<typename decltype(std::span{cont})::element_type>
。您可以为其添加额外的或不同的约束,或者将这些约束与您自己的概念结合起来。
std::span