将概念传递给函数
Passing a concept to a function
由于概念被定义为编译时谓词,是否也可以将这些谓词实际重用于编译时算法?例如,是否可以检查元组中的所有类型是否都符合一个概念?据我所知,不可能以任何方式将概念传递给函数,这让我又回到了在这些情况下使用模板。
#include <type_traits>
template<typename T>
concept FloatLike = std::is_same_v<T, float>;
struct IsFloat
{
template<typename U>
constexpr static bool test()
{
return FloatLike<U>;
}
};
template<typename Predicate, typename... T>
constexpr bool all_types()
{
return (Predicate::template test<T>() && ...);
}
int main()
{
static_assert(all_types<IsFloat, float, float>());
static_assert(!all_types<IsFloat, float, int>());
}
我想做的是这样的,所以我不必一直把概念包装起来就可以使用它:
template<concept Predicate, typename... T>
constexpr bool all_types()
{
return (Predicate<T> && ...);
}
int main()
{
static_assert(all_types<FloatLike, float, float>());
static_assert(!all_types<FloatLike, float, int>());
}
有什么方法可以更接近这个吗?
Is there any way to get closer to this?
嗯,不,不是真的。不在 C++20 中。今天的语言中没有模板概念参数的概念。即使是变量模板也不能用作模板参数。所以如果有一个概念开始,我们就不能避免包装。
但我们能做的是编写更简单的包装器。如果我们同意使用 "old style" 类型特征作为谓词,特别是那些行为类似于 std::integral_constant
s 的特征,那么我们可以拥有非常简洁的 "concept" 定义,可以用作谓词。
template<typename T>
using FloatLike = std::is_same<T, float>;
template<template <typename> class Predicate, typename... T>
constexpr bool all_types()
{
return (Predicate<T>{} && ...);
}
据我所知,它和 it can get 一样好。
如果你的目标是"check whether all types in a tuple conform to a concept",那么你可以这样做:
// concept to check if all types in Ts are the same as T
template<typename T, typename... Ts>
concept AllSame = (std::is_same_v<T,Ts> && ...);
// function only accepts floats as template parameters
template<AllSame<float>... Floats>
constexpr void float_foo()
{
}
// function only accepts ints as template parameters
template<AllSame<int>... Ints>
constexpr void int_foo()
{
}
// function only accepts T as template parameters
template<typename T, AllSame<T>... Ts>
constexpr void foo()
{
}
int main()
{
int_foo<int, int, int>();
// int_foo<int, int, double>(); // fails to compile
float_foo<float, float, float>();
// float_foo<float, float, int>(); // fails to compile
foo<int, int, int, int>();
// foo<int, int, int, float>(); // fails to compile
foo<double, double, double, double>();
// foo<double, double, double, int>(); // fails to compile
}
LIVE DEMO
由于概念被定义为编译时谓词,是否也可以将这些谓词实际重用于编译时算法?例如,是否可以检查元组中的所有类型是否都符合一个概念?据我所知,不可能以任何方式将概念传递给函数,这让我又回到了在这些情况下使用模板。
#include <type_traits>
template<typename T>
concept FloatLike = std::is_same_v<T, float>;
struct IsFloat
{
template<typename U>
constexpr static bool test()
{
return FloatLike<U>;
}
};
template<typename Predicate, typename... T>
constexpr bool all_types()
{
return (Predicate::template test<T>() && ...);
}
int main()
{
static_assert(all_types<IsFloat, float, float>());
static_assert(!all_types<IsFloat, float, int>());
}
我想做的是这样的,所以我不必一直把概念包装起来就可以使用它:
template<concept Predicate, typename... T>
constexpr bool all_types()
{
return (Predicate<T> && ...);
}
int main()
{
static_assert(all_types<FloatLike, float, float>());
static_assert(!all_types<FloatLike, float, int>());
}
有什么方法可以更接近这个吗?
Is there any way to get closer to this?
嗯,不,不是真的。不在 C++20 中。今天的语言中没有模板概念参数的概念。即使是变量模板也不能用作模板参数。所以如果有一个概念开始,我们就不能避免包装。
但我们能做的是编写更简单的包装器。如果我们同意使用 "old style" 类型特征作为谓词,特别是那些行为类似于 std::integral_constant
s 的特征,那么我们可以拥有非常简洁的 "concept" 定义,可以用作谓词。
template<typename T>
using FloatLike = std::is_same<T, float>;
template<template <typename> class Predicate, typename... T>
constexpr bool all_types()
{
return (Predicate<T>{} && ...);
}
据我所知,它和 it can get 一样好。
如果你的目标是"check whether all types in a tuple conform to a concept",那么你可以这样做:
// concept to check if all types in Ts are the same as T
template<typename T, typename... Ts>
concept AllSame = (std::is_same_v<T,Ts> && ...);
// function only accepts floats as template parameters
template<AllSame<float>... Floats>
constexpr void float_foo()
{
}
// function only accepts ints as template parameters
template<AllSame<int>... Ints>
constexpr void int_foo()
{
}
// function only accepts T as template parameters
template<typename T, AllSame<T>... Ts>
constexpr void foo()
{
}
int main()
{
int_foo<int, int, int>();
// int_foo<int, int, double>(); // fails to compile
float_foo<float, float, float>();
// float_foo<float, float, int>(); // fails to compile
foo<int, int, int, int>();
// foo<int, int, int, float>(); // fails to compile
foo<double, double, double, double>();
// foo<double, double, double, int>(); // fails to compile
}