如何编写一个概念来检查 std::tuple 的所有类型中的内部类型?

问题描述 投票:0回答:3

继续我对 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
的类型不同,所以我预计会出现概念错误不满足,但没有报错

c++ metaprogramming c++20 c++-concepts
3个回答
1
投票

这段代码,

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

0
投票

你的概念

same_inner
没有效果,可以用任何类型编译。

你应该在你的

same_as
概念限制,而不是
same_inner
,它总是编译成功,你可以从中获取结果。
is_same

这可能会解决问题。

有关详细信息,请参阅

此处


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>

演示

© www.soinside.com 2019 - 2024. All rights reserved.