我想创建一个concept
,它允许我为每个read
和write
从/到数据容器定义一个函数。数据容器应该有一个.data()
和一个.size()
方法,以便在阅读/写之前检查大小。另外,对于写函数,数据类型应该标记为const
,这样我就不能错误地更改所指向的变量或其内容。
例如,我希望支持像std::vector
这样的数据类型,如果可能的话,如果我只将指针和大小分开,则支持std::span
。
为了检查容器是否同时具有这两种方法,我提出了
template <typename T>
concept HasDataAndSize =
requires(T t) { t.size(); } && requires(T t) { t.data(); } &&
requires(T t) { std::is_same<decltype(t.size()), std::size_t>::value; } &&
requires(T t) { std::is_pointer<decltype(t.data())>::value; };
问题是如果我创建一个函数
template<HasDataAndSize T> void write(const T& data)
该数据对于std::span
对象是可写的。
另外,如果我创建一个阅读函数
template<HasDataAndSize T> void read(T read)
std::span
可以工作,但是像std::vector
这样的容器被复制,并且可以在函数之外访问更改。如果我使用一个T &
,std::vector
可以工作,但我不能从临时的read(std::span{data_ptr, data_size})
使用std::span<>
目前我认为我只能允许容器,这不允许改变它里面的ptr,像std::span
可以。然而,我的概念仍然让std::span
通过,我不知道如何阻止它。
或者有更好的解决方案吗?/为什么我应该使用另一种设计?
我创建了一个小例子来玩:https://godbolt.org/z/nT7jWve91
为了澄清,我写的是一个库,它必须将来自不同数据容器的数据写入文件或将文件中的内容读取到数据中。
1条答案
按热度按时间nle07wnf1#
让我们从这个开始:
首先也是最重要的,没有必要将所有内容分离到自己的requires-expression中,这增加了大量语法,但没有任何好处。这相当于:
接下来,上面标记为
(1)
和(2)
的行就不会了。做你认为他们会做的事事实上,他们什么都不做。您要检查的是 expressionstd::is_same<T, U>::value
是一个有效的表达式--当然是有效的,不管T
和U
是什么。实际上并不是检查类型是否相同。为了做到这一点,你需要在表达式前面加上
requires
,或者使用复合需求语法:一旦我们到了那里,你还有另一个问题
这是检查
T
是否有data()
和size()
,但这不是真正相关的问题,因为data
不是T
,而是T const
。在这里,你需要做:或引入助手概念:
或者使用转发引用(这将确保
T
以对概念有用的方式推导):最后,这更像是一个API问题,但最好完全避免这个问题,只写:
虽然一般来说我并不真正理解这里的API应该做什么。