英文:
How to write a concept that checks for an inner type in all the types of a std::tuple?
问题
我预期会出现概念不满足的错误,因为type_1::inner
和type_2::inner
具有不同的类型,但是没有报告任何错误。
英文:
Continuing with my studies on C++ 20 concepts, I am trying to write a concept
that would assure that every type in a std::tuple
has inner type defined with a specific name, and that all the inner
s have the same type.
I tried this:
#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;
}
I expected an error of concept not satisfied because type_1::inner
and type_2::inner
have different types, but no errors were reported.
答案1
得分: 2
Your concept same_inner
have no effect and can be compiled with any types.
You should use same_as
concept in your
same_inner
concept restriction, instead of is_same
, which always compiles successfully and you cound fetch the result from.
template<typename T, typename U>
concept same_inner = same_as<typename T::inner, typename U::inner>;
This may fix the problem.
See here for detailed informations.
英文:
Your concept same_inner
have no effect and can be compiled with any types.
You should use same_as
concept in your
same_inner
concept restriction, instead of is_same
, which always compiles successfully and you cound fetch the result from.
template<typename T, typename U>
concept same_inner = same_as<typename T::inner, typename U::inner>;
This may fix the problem.
See here for detailed informations.
答案2
得分: 2
Here is the translated code:
模板 <typename t, typename... t_type_model>
概念 container_model = 需要(t p_t) {
需要((same_inner<t_type_model,
std::tuple_element_t<0, std::tuple<t_type_model...>>> &&
...));
};
这段代码实际上没有测试第二个需要子句中的条件。
正确的形式是
```c++
模板 <typename t, typename u>
概念 same_inner = std::is_same_v<typename t::inner, typename u::inner>;
或者,
模板 <typename t, typename u>
概念 same_inner = 需要 (t i, u j) {
需要 (std::same_as<typename t::inner, typename u::inner>);
};
这曾经让我感到困惑。
更新
container_model
概念
模板 <typename t, typename... t_type_model>
概念 container_model = 需要(t p_t) {
需要((same_inner<t_type_model,
std::tuple_element_t<0, std::tuple<t_type_model...>>> &&
...));
};
需要像这样
模板 <typename t, typename... t_type_model>
概念 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
更改为以下方式之一:
模板 <type_model... t_type_model>
void f(container_model<container<t_type_model...>> auto) {}
摘要
为了明确,以下是带有更改的所有代码,此代码无法编译,因为未满足 container_model
约束。
#include <concepts>
#include <string>
#include <tuple>
模板 <typename t>
概念 type_model = 需要(t p_t) {
typename t::inner;
};
模板 <typename t, typename u>
概念 same_inner = std::is_same_v<typename std::decay_t<t>::inner,
typename std::decay_t<u>::inner>;
模板 <typename t, typename... t_type_model>
概念 container_model = (same_inner<t, t_type_model> && ...);
结构体 type_1 {
using inner = int;
};
结构体 type_2 {
using inner = std::string;
};
模板 <type_model... t_type_models> 结构体 container {};
模板 <type_model... t_type_model>
void f(container_model<container<t_type_model...>> auto) {}
使用 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);
返回 0;
}
新的更新
以前的更新在最好情况下是不完整的。以下是我认为可以实现所需概念的最新代码。
示例代码
#include <concepts>
#include <string>
#include <tuple>
// 类型 T 是否具有名为 `inner` 的内部类型。
模板<类 T>
概念 Inner = 需要(T x) {
typename T::inner;
};
// T 和 U 是否具有相同的内部类型。
模板<类 T, 类 U>
概念 SameInner = Inner<T> 和 Inner<U> 和 std::is_same_v<typename T::inner, typename U::inner>;
// 用于检查所有模板参数是否满足 Inner 概念以及所有配对是否满足 SameInner 的辅助程序。
模板<typename>
结构体 inner_container_impl : std::false_type {};
模板<模板<typename...> 类 Tp, Inner T>
结构体 inner_container_impl<Tp<T>> {
static constexpr bool value = true;
};
模板<模板<typename...> 类 Tp, Inner T, Inner... Ts>
结构体 inner_container_impl<Tp<T, Ts...>> {
static constexpr bool value = (SameInner<T, Ts> 和 ...);
};
// 该概念只是使用辅助程序。
模板<类 T>
概念 InnerContainer = inner_container_impl<T>::value;
// 测试类型。
结构体 type_1 {
using inner = int;
};
结构体 type_2 {
using inner = std::string;
};
结构体 type_3 {
using inner = int;
};
模板<Inner... Ts> 结构体 container {};
void f(InnerContainer auto) {}
使用 containers_X = std::tuple<type_1, type_2>;
使用 containers_Y = std::tuple<type_1, type_3>;
static_assert(not SameInner<type_1, type_2>);
static_assert(not InnerContainer<std::tuple<type_1, type_2>>);
static_assert(not InnerContainer<containers_X>);
int main() {
containers_X _x;
f(_x);
containers_Y _y;
f(_y);
返回 0;
}
输出
/work/so/scratch/src/p4.cpp:64:5: 错误: 没有匹配
<details>
<summary>英文:</summary>
This code,
```c++
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...>>> &&
...));
};
does not actually test the condition within the second requires clause.
The correct formulation is
template <typename t, typename u>
concept same_inner = std::is_same_v<typename t::inner, typename u::inner>;
or,
template <typename t, typename u>
concept same_inner = requires (t i, u j) {
requires (std::same_as<typename t::inner, typename u::inner>);
};
This has tripped me up in the past.
Update
The container_model
concept
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...>>> &&
...));
};
need to be something like
template <typename t, typename... t_type_model>
concept container_model = (same_inner<t, t_type_model> && ...);
At this point, the following assertions should hold:
static_assert(not same_inner<type_1, type_2>);
static_assert(not container_model<type_1, type_2>);
When the function f
is invoked, it is passed container<type_1, type_2>
which qualifies as a container_model
because it gets translated to container_model<container<type_t, type_2>>
which returns true
.
One possible fix is to change f
to something like
template <type_model... t_type_model>
void f(container_model<container<t_type_model...>> auto) {}
Summary
Just to be clear, here is all the code with changes and this code fails to compile because the container_model
constraints are not satisfied.
#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;
}
New Update
The previous update was incomplete at best. Here is the latest code which I believe enforces the desired concepts.
Sample Code
#include <concepts>
#include <string>
#include <tuple>
// Does T have an inner type named `inner`.
template<class T>
concept Inner = requires(T x) {
typename T::inner;
};
// Do T and U have same inner type.
template<class T, class U>
concept SameInner = Inner<T> and Inner<U> and std::is_same_v<typename T::inner, typename U::inner>;
// Helper for checking that all template parameters satisfy Inner
// concept and all pairs satisfy SameInner.
template<typename>
struct inner_container_impl : std::false_type {};
template<template<typename...> class Tp, Inner T>
struct inner_container_impl<Tp<T>> {
static constexpr bool value = true;
};
template<template<typename...> class Tp, Inner T, Inner... Ts>
struct inner_container_impl<Tp<T, Ts...>> {
static constexpr bool value = (SameInner<T, Ts> and ...);
};
// The concept just use the helper.
template<class T>
concept InnerContainer = inner_container_impl<T>::value;
// Test types.
struct type_1 {
using inner = int;
};
struct type_2 {
using inner = std::string;
};
struct type_3 {
using inner = int;
};
template<Inner... Ts> struct container {};
void f(InnerContainer auto) {}
using containers_X = std::tuple<type_1, type_2>;
using containers_Y = std::tuple<type_1, type_3>;
static_assert(not SameInner<type_1, type_2>);
static_assert(not InnerContainer<std::tuple<type_1, type_2>>);
static_assert(not InnerContainer<containers_X>);
int main() {
containers_X _x;
f(_x);
containers_Y _y;
f(_y);
return 0;
}
Output
/work/so/scratch/src/p4.cpp:64:5: error: no matching
function for call to 'f'
f(_x);
^
/work/so/scratch/src/p4.cpp:52:6: note: candidate
template ignored: constraints not satisfied [with auto:1 = std::tuple<type_1,
type_2>]
void f(InnerContainer auto) {}
^
/work/so/scratch/src/p4.cpp:52:8: note: because
'std::tuple<type_1, type_2>' does not satisfy 'InnerContainer'
void f(InnerContainer auto) {}
^
/work/so/scratch/src/p4.cpp:35:26: note: because
'inner_container_impl<tuple<type_1, type_2> >::value' evaluated to false
concept InnerContainer = inner_container_impl<T>::value;
^
1 error generated.
答案3
得分: 0
除了@RandomBits的实现之外,我认为我找到了另一个实现:
#include <concepts>
#include <iomanip>
#include <iostream>
#include <string>
#include <tuple>
template <typename t>
concept has_inner = requires(t p_t) {
typename t::inner;
};
template <has_inner t_1, has_inner t_2> struct same_inner {
static constexpr bool yes{
std::is_same_v<typename t_1::inner, typename t_2::inner>};
};
template <typename t_tuple, size_t t_idx = std::tuple_size_v<t_tuple> - 1>
struct check_if_all_has_inner {
static constexpr bool yes{
same_inner<typename std::tuple_element_t<t_idx, t_tuple>,
typename std::tuple_element_t<t_idx - 1, t_tuple>>::yes &&
check_if_all_has_inner<t_tuple, t_idx - 1>::yes};
};
template <typename t_tuple> struct check_if_all_has_inner<t_tuple, 0> {
static constexpr bool yes{true};
};
template <typename t_tuple>
concept container_model = (std::tuple_size_v<t_tuple> > 0) &&
(check_if_all_has_inner<t_tuple>::yes);
struct type_1 {
using inner = int;
};
struct type_2 {
using inner = std::string;
};
struct type_3 {
using inner = std::string;
};
struct type_4 {};
void f(container_model auto) {}
int main() {
/// ERROR 1 if code below uncommented
// f(std::tuple<type_1, type_2>{});
f(std::tuple<type_2, type_3>{});
{
std::cout << std::boolalpha
<< check_if_all_has_inner<std::tuple<type_1, type_2>>::yes
<< std::endl;
}
{
std::cout << std::boolalpha
<< check_if_all_has_inner<std::tuple<type_3, type_2>>::yes
<< std::endl;
}
/// ERROR 2 if code below uncommented
// {
// std::cout << std::boolalpha
// << check_if_all_has_inner<std::tuple<type_3, type_2,
// type_4>>::yes
// << std::endl;
// }
return 0;
}
其中ERROR 1
是:
main.cpp:52:3: No matching function for call to 'f'
main.cpp:47:6: candidate template ignored: constraints not satisfied [with auto:1 = std::tuple<type_1, type_2>] main.cpp:47:8: because 'std::tuple<type_1, type_2>' does not satisfy 'container_model' main.cpp:31:28: because 'check_if_all_has_inner<tuple<type_1, type_2> >::yes' evaluated to false
而ERROR 2
是:
main.cpp:71:78: In instantiation of static data member 'check_if_all_has_inner<std::tuple<type_3, type_2, type_4>, 2>::yes' requested here main.cpp:20:7: In instantiation of static data member 'check_if_all_has_inner<std::tuple<type_3, type_2, type_4>, 2>::yes' requested here
main.cpp:20:7: Constraints not satisfied for class template 'same_inner' [with t_1 = type_4, t_2 = type_2] main.cpp:71:78: in instantiation of static data member 'check_if_all_has_inner<std::tuple<type_3, type_2, type_4>, 2>::yes' requested here main.cpp:12:11: because 'type_4' does not satisfy 'has_inner' main.cpp:9:15: because 'typename t::inner' would be invalid: no type named 'inner' in 'type_4'
如果需要进一步解释或翻译其他部分,请告诉我。
英文:
Besides @RandomBits implementation, I believe I found another one:
#include <concepts>
#include <iomanip>
#include <iostream>
#include <string>
#include <tuple>
template <typename t>
concept has_inner = requires(t p_t) {
typename t::inner;
};
template <has_inner t_1, has_inner t_2> struct same_inner {
static constexpr bool yes{
std::is_same_v<typename t_1::inner, typename t_2::inner>};
};
template <typename t_tuple, size_t t_idx = std::tuple_size_v<t_tuple> - 1>
struct check_if_all_has_inner {
static constexpr bool yes{
same_inner<typename std::tuple_element_t<t_idx, t_tuple>,
typename std::tuple_element_t<t_idx - 1, t_tuple>>::yes &&
check_if_all_has_inner<t_tuple, t_idx - 1>::yes};
};
template <typename t_tuple> struct check_if_all_has_inner<t_tuple, 0> {
static constexpr bool yes{true};
};
template <typename t_tuple>
concept container_model = (std::tuple_size_v<t_tuple> > 0) &&
(check_if_all_has_inner<t_tuple>::yes);
struct type_1 {
using inner = int;
};
struct type_2 {
using inner = std::string;
};
struct type_3 {
using inner = std::string;
};
struct type_4 {};
void f(container_model auto) {}
int main() {
/// ERROR 1 if code below uncommented
// f(std::tuple<type_1, type_2>{});
f(std::tuple<type_2, type_3>{});
{
std::cout << std::boolalpha
<< check_if_all_has_inner<std::tuple<type_1, type_2>>::yes
<< std::endl;
}
{
std::cout << std::boolalpha
<< check_if_all_has_inner<std::tuple<type_3, type_2>>::yes
<< std::endl;
}
/// ERROR 2 if code below uncommented
// {
// std::cout << std::boolalpha
// << check_if_all_has_inner<std::tuple<type_3, type_2,
// type_4>>::yes
// << std::endl;
// }
return 0;
}
Where ERROR 1
is
main.cpp:52:3: No matching function for call to 'f'
main.cpp:47:6: candidate template ignored: constraints not satisfied [with
auto:1 = std::tuple<type_1, type_2>] main.cpp:47:8: because 'std::tuple<type_1,
type_2>' does not satisfy 'container_model' main.cpp:31:28: because
'check_if_all_has_inner<tuple<type_1, type_2> >::yes' evaluated to false
and ERROR 2
is
main.cpp:71:78: In instantiation of static data member
'check_if_all_has_inner<std::tuple<type_3, type_2, type_4>, 2>::yes' requested
here main.cpp:20:7: In instantiation of static data member
'check_if_all_has_inner<std::tuple<type_3, type_2, type_4>, 2>::yes' requested
here
main.cpp:20:7: Constraints not satisfied for class template 'same_inner' [with
t_1 = type_4, t_2 = type_2] main.cpp:71:78: in instantiation of static data
member 'check_if_all_has_inner<std::tuple<type_3, type_2, type_4>, 2>::yes'
requested here main.cpp:12:11: because 'type_4' does not satisfy 'has_inner'
main.cpp:9:15: because 'typename t::inner' would be invalid: no type named
'inner' in 'type_4'
答案4
得分: 0
Sure, here is the translated code portion:
[RandomBits][1],基于[这个][2]主题,我想我提出了另一种实现:
#include <concepts>
#include <iomanip>
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
// 检查类型是否为std::tuple
template <class t_tuple, std::size_t t_idx>
concept has_tuple_element = requires(t_tuple t) {
typename std::tuple_element_t<t_idx, std::remove_const_t<t_tuple>>;
{
get<t_idx>(t)
} -> std::convertible_to<const std::tuple_element_t<t_idx, t_tuple> &>;
};
template <class t_tuple>
concept tuple_like = !std::is_reference_v<t_tuple> && requires(t_tuple t) {
typename std::tuple_size<t_tuple>::type;
requires std::derived_from<
std::tuple_size<t_tuple>,
std::integral_constant<std::size_t, std::tuple_size_v<t_tuple>>>;
}
&& []<std::size_t... t_idx>(std::index_sequence<t_idx...>) {
return (has_tuple_element<t_tuple, t_idx> && ...);
}
(std::make_index_sequence<std::tuple_size_v<t_tuple>>());
// 检查元组中索引位置的类型是否具有'inner'类型,如果前一个索引位置也具有'inner'类型,且两个'inner'类型相同
template <size_t t_idx, typename t_tuple> struct same_inner {
static constexpr bool yes{
std::is_same_v<typename std::tuple_element_t<t_idx, t_tuple>::inner,
typename std::tuple_element_t<t_idx - 1, t_tuple>::inner>};
};
template <typename t_tuple> struct same_inner<0, t_tuple> {
static constexpr bool yes{true};
};
// std::tuple,其中所有类型具有'inner'类型
template <typename t_tuple>
concept container_model = (tuple_like<t_tuple>)&&[]<std::size_t... t_idx>(
std::index_sequence<t_idx...>) {
return (same_inner<t_idx, t_tuple>::yes && ...);
}
(std::make_index_sequence<std::tuple_size_v<t_tuple>>());
// 示例
struct type_1 {
using inner = int;
};
struct type_2 {
using inner = std::string;
};
struct type_3 {
using inner = std::string;
};
struct type_4 {};
// 处理所有类型具有'inner'类型的std::tuple的函数
void f(container_model auto) {}
int main() {
f(std::tuple<type_2, type_3>{});
/// 如果取消下面的代码的注释,将会出错
// f(std::tuple<type_1, type_2>{});
/// 如果取消下面的代码的注释,将会出错
// f(std::tuple<type_2, type_4>{});
/// 如果取消下面的代码的注释,将会出错
// f(int{3});
return 0;
}
<details>
<summary>英文:</summary>
[RandomBits][1], based on [this][2] topic, I think I came up with another implentation:
#include <concepts>
#include <iomanip>
#include <iostream>
#include <string>
#include <tuple>
#include <type_traits>
// checking if a type is a std::tuple
template <class t_tuple, std::size_t t_idx>
concept has_tuple_element = requires(t_tuple t) {
typename std::tuple_element_t<t_idx, std::remove_const_t<t_tuple>>;
{
get<t_idx>(t)
} -> std::convertible_to<const std::tuple_element_t<t_idx, t_tuple> &>;
};
template <class t_tuple>
concept tuple_like = !std::is_reference_v<t_tuple> && requires(t_tuple t) {
typename std::tuple_size<t_tuple>::type;
requires std::derived_from<
std::tuple_size<t_tuple>,
std::integral_constant<std::size_t, std::tuple_size_v<t_tuple>>>;
}
&&[]<std::size_t... t_idx>(std::index_sequence<t_idx...>) {
return (has_tuple_element<t_tuple, t_idx> && ...);
}
(std::make_index_sequence<std::tuple_size_v<t_tuple>>());
// checking if a type in a index of a tuple has 'inner' type, if the index
// before it has a 'inner' type, and if both 'inner' types are identical
template <size_t t_idx, typename t_tuple> struct same_inner {
static constexpr bool yes{
std::is_same_v<typename std::tuple_element_t<t_idx, t_tuple>::inner,
typename std::tuple_element_t<t_idx - 1, t_tuple>::inner>};
};
template <typename t_tuple> struct same_inner<0, t_tuple> {
static constexpr bool yes{true};
};
// std::tuple where all the types have a 'inner' type
template <typename t_tuple>
concept container_model = (tuple_like<t_tuple>)&&[]<std::size_t... t_idx>(
std::index_sequence<t_idx...>) {
return (same_inner<t_idx, t_tuple>::yes && ...);
}
(std::make_index_sequence<std::tuple_size_v<t_tuple>>());
// examples
struct type_1 {
using inner = int;
};
struct type_2 {
using inner = std::string;
};
struct type_3 {
using inner = std::string;
};
struct type_4 {};
// function that will handle a std::tuple where all the types have a 'inner'
// type
void f(container_model auto) {}
int main() {
f(std::tuple<type_2, type_3>{});
/// ERROR if code below uncommented
// f(std::tuple<type_1, type_2>{});
/// ERROR if code below uncommented
// f(std::tuple<type_2, type_4>{});
/// ERROR if code below uncommented
// f(int{3});
return 0;
}
[1]: https://stackoverflow.com/users/1202808/randombits
[2]: https://stackoverflow.com/questions/68443804/c20-concept-to-check-tuple-like-types
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论