继续我对 C++ 20 概念的研究,我正在尝试编写一个
concept
以确保 std::tuple
中的每个类型都具有用特定名称定义的内部类型,并且所有 inner
具有相同的类型。
我试过这个:
#include <concepts>
#include <string>
#include <tuple>
template <typename t>
concept type_model = requires(t p_t) {
typename t::inner;
};
template <typename t, typename u>
concept same_inner = requires(t p_t, u p_u) {
std::is_same_v<typename t::inner, typename u::inner>;
};
template <typename t, typename... t_type_model>
concept container_model = requires(t p_t) {
requires((same_inner<t_type_model,
std::tuple_element_t<0, std::tuple<t_type_model...>>> &&
...));
};
struct type_1 {
using inner = int;
};
struct type_2 {
using inner = std::string;
};
template <type_model... t_type_models> struct container {};
template <type_model... t_type_model>
void f(container_model<t_type_model...> auto) {}
using containers_X = container<type_1, type_2>;
int main() {
containers_X _x;
f(_x);
return 0;
}
因为
type_1::inner
和type_2::inner
的类型不同,所以我预计会出现概念错误不满足,但没有报错
这段代码,
template <typename t, typename... t_type_model>
concept container_model = requires(t p_t) {
requires((same_inner<t_type_model,
std::tuple_element_t<0, std::tuple<t_type_model...>>> &&
...));
};
实际上并没有测试第二个 requires 子句中的条件。
正确的表述是
template <typename t, typename u>
concept same_inner = std::is_same_v<typename t::inner, typename u::inner>;
或者,
template <typename t, typename u>
concept same_inner = requires (t i, u j) {
requires (std::same_as<typename t::inner, typename u::inner>);
};
这在过去让我绊倒了。
更新
container_model
概念
template <typename t, typename... t_type_model>
concept container_model = requires(t p_t) {
requires((same_inner<t_type_model,
std::tuple_element_t<0, std::tuple<t_type_model...>>> &&
...));
};
需要像
template <typename t, typename... t_type_model>
concept container_model = (same_inner<t, t_type_model> && ...);
此时,以下断言应该成立:
static_assert(not same_inner<type_1, type_2>);
static_assert(not container_model<type_1, type_2>);
当函数
f
被调用时,它被传递 container<type_1, type_2>
,它有资格作为 container_model
因为它被翻译成 container_model<container<type_t, type_2>>
返回 true
.
一个可能的解决方法是将
f
更改为类似
template <type_model... t_type_model>
void f(container_model<container<t_type_model...>> auto) {}
需要说明的是,这里是所有更改过的代码,并且此代码无法编译,因为不满足
container_model
约束条件。
#include <concepts>
#include <string>
#include <tuple>
template <typename t>
concept type_model = requires(t p_t) {
typename t::inner;
};
template <typename t, typename u>
concept same_inner = std::is_same_v<typename std::decay_t<t>::inner,
typename std::decay_t<u>::inner>;
template <typename t, typename... t_type_model>
concept container_model = (same_inner<t, t_type_model> && ...);
struct type_1 {
using inner = int;
};
struct type_2 {
using inner = std::string;
};
template <type_model... t_type_models> struct container {};
template <type_model... t_type_model>
void f(container_model<container<t_type_model...>> auto) {}
using containers_X = container<type_1, type_2>;
static_assert(not same_inner<type_1, type_2>);
static_assert(not container_model<type_1, type_2>);
int main() {
containers_X _x;
f(_x);
return 0;
}
template<typename T, typename U>
concept same_inner = same_as<T::inner, U::inner>;
可以简单地重写为:
container_model
其次,针对以下
template <typename t, typename... t_type_model>
concept container_model =
(std::same_as<typename t::inner, typename t_type_model::inner> && ...);
当你传入类型为
template <type_model... t_type_model>
void f(container_model<t_type_model...> auto) {}
的
_x
时,编译器无法推导出模板包的类型,这使得参数包总是空的,并且总是满足约束。 您可能想要重新定义函数签名以获取参数包并检查:
container<type_1, type_2>