SKN*_*KNB 18 c++ templates template-meta-programming c++-concepts c++20
假设我有两个课程:
\ntemplate <typename X, typename Y>\nclass Functor {};\n\ntemplate <typename Start, typename End, typename ...Functors>\nclass Template {};\nRun Code Online (Sandbox Code Playgroud)\nTemplate有以下限制:
全部Functors必须是类型Functor
所有内容都Functor必须按链式顺序排列,这样
Functor必须Start作为其第一个参数Functor必须End作为第二个参数Functor\ 的第一个参数是前面它的第二个参数Functor例如Functor<A,B>, Functor<B, C>, Functor<C, D>, ...等等。
从...开始:char
结尾为:long
Template<char, long, Functor<char, A>, Functor<A, B>, Functor<B, C>, Functor<C, long>> t;
1 2 3 4\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xbc\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\x80\xe2\x94\xa4\nargument: char A B C long\nFunctor #\n = 1 Functor<char, A>,\n 2 Functor<A, B>,\n 3 Functor<B, C>,\n 4 Functor<C, long>\nRun Code Online (Sandbox Code Playgroud)\nnamespace ns\n{\n template <typename X, typename Y = X>\n class Functor\n {\n public:\n using first = X;\n using second = Y;\n Functor(X lVal) : x(lVal) {}\n private:\n X x;\n };\n\n template <typename Start, typename End, typename ...Functors>\n requires(std::is_convertible_v<Functors, Functor> && ...) //error\n class Template\n {\n // How does one use `std::is_convertible_v` on\n // an un-specialized template class?\n };\n\n template <typename Start, typename End>\n class Template<Start, End, Functor<Start, End>>\n {};\n}\nRun Code Online (Sandbox Code Playgroud)\n问题:
\nstd::is_convertible(或任何其他元编程特征)?Jar*_*d42 14
通过(ab)使用运算符重载,您可能会这样做
// Used std::type_identity as wrapper
// Operator+, but no implementation needed
template <typename A, typename B, typename C>
std::type_identity<Functor<A, C>>
operator +(std::type_identity<Functor<A, B>>, std::type_identity<Functor<B, C>>);
Run Code Online (Sandbox Code Playgroud)
然后只需检查操作是否“有效”。
template <typename Start, typename End, typename... Functors>
requires(std::is_same_v<std::type_identity<Functor<Start, End>>,
decltype((std::type_identity<Functors>{} + ...))>)
class Template {
//...
};
Run Code Online (Sandbox Code Playgroud)
Ted*_*gmo 10
您可以首先添加类型特征来检查模板参数的类型Functor:
template <typename X, typename Y>
class Functor {
public:
using first_type = X;
using second_type = Y;
};
// type trait to check if a type is a Functor
template <class...>
struct is_Functor : std::false_type {};
template <class X, class Y>
struct is_Functor<Functor<X, Y>> : std::true_type {};
template <class T>
inline constexpr bool is_Functor_v = is_Functor<T>::value;
Run Code Online (Sandbox Code Playgroud)
然后要求它们实际上都是Functor使用requires带有折叠表达式的子句的类型:
// Require Functors
template <typename Start, typename End, typename... Functors>
requires(is_Functor_v<Functors> && ...)
class Template {
Run Code Online (Sandbox Code Playgroud)
然后断言第一个Functor作为Start第一个参数,最后一个Functor作为End第二个参数:
// helper type to get a Functor (type) at a specific index:
template <std::size_t I>
using funcat = typename std::tuple_element_t<I, std::tuple<Functors...>>;
static_assert(std::is_same_v<
Start, typename funcat<0>::first_type>);
static_assert(std::is_same_v<
End, typename funcat<sizeof...(Functors) - 1>::second_type>);
Run Code Online (Sandbox Code Playgroud)
然后使用 lambda 和折叠表达式检查每个参数的第二个参数Functor与下一个参数的第一个参数的类型相同:Functor
static_assert([]<std::size_t... I>(std::index_sequence<I...>) {
return (... && std::is_same_v<typename funcat<I>::second_type,
typename funcat<I + 1>::first_type>);
}(std::make_index_sequence<sizeof...(Functors) - 1>()));
Run Code Online (Sandbox Code Playgroud)
如果您喜欢这样的声明,您也可以使用concept类型特征:is_Functor
template <class T>
concept functor = is_Functor_v<T>;
template <typename Start, typename End, functor... Functors>
class Template {
//
};
Run Code Online (Sandbox Code Playgroud)
至于第二个问题,“如何std::is_convertible在非专用模板类上使用(或任何其他元编程特征)?” ,您可以再次创建一个类型特征,或者concept检查 a 是否Functor可以由提供的类型实例化,而无需显式提供任何模板参数。
例子concept:
template <class F, template <class...> class T>
concept deducible = requires(F&& f) {
T(std::forward<F>(f));
};
Run Code Online (Sandbox Code Playgroud)
然后,类模板需要使用Functor每个实际模板参数将转换为的类型:
template <typename Start, typename End, deducible<Functor>... Functors>
class Template {
template <std::size_t I>
using funcat =
std::tuple_element_t<I,
std::tuple<decltype(Functor(std::declval<Functors>()))...>>;
// converted to Functor ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
static_assert(std::is_same_v<
Start, typename funcat<0>::first_type>,
"First Functor::first_type != Start");
static_assert(std::is_same_v<
End, typename funcat<sizeof...(Functors) - 1>::second_type>,
"Last Functor::second_type != End");
static_assert([]<std::size_t... I>(std::index_sequence<I...>) {
return (... && std::is_same_v<typename funcat<I>::second_type,
typename funcat<I + 1>::first_type>);
}(std::make_index_sequence<sizeof...(Functors) - 1>()),
"Functor_n<..., T> != Functor_n+1<T, ...>");
};
Run Code Online (Sandbox Code Playgroud)
演示,其中 astd::pair<X, Y>可转换为 a Functor<X, Y>(奇怪的转换,但这只是一个示例)。
作为单个需求表达式:
requires requires(Functors... f) {
[]<typename... X, typename... Y>(Functor<X, Y>&...)
requires std::same_as<void(Start, Y...), void(X..., End)> {
}(f...);
}
Run Code Online (Sandbox Code Playgroud)
首先,我们推导出每个 的X和;如果其中任何一个不是 的实例,则此步骤将失败。(严格来说,它还允许从; 派生的类型,为了防止这种情况,您可以使用。)然后,我们检查类型链是否与链相同(注意:不是!);如果形成从到 的链,则这将精确成立。YFunctorFunctorsFunctorFunctor<X, Y>std::type_identityStart, Y...X..., End Start, X...<X, Y>...StartEnd
请注意,它也将保留 for <Start, End, Functor<Start, End>>,您已将其列为单独的情况,并且 for <Start, Start>,即如果Start和End是相同类型,则它将允许链为空;如果您想禁止这样做,您可以添加sizeof...(Functors) != 0u一个额外的约束。
我使用函数类型作为类型列表,它很简洁,但确实存在类型衰减的潜在缺点;您同样可以使用eg std::tuple,这将允许放宽对eg的约束std::convertible_to(std::tuple<T...>可转换为std::tuple<U...>当且仅当每个T都可转换为其各自的U)。
如果传递了无效的链,gcc 将输出一个以类似以下内容结尾的错误:
note: the expression 'is_same_v<_Tp, _Up> [with _Tp = void(char, A, C, C, int); _Up = void(char, A, B, C, long int)]' evaluated to 'false'
Run Code Online (Sandbox Code Playgroud)
演示。
编写约束的一种稍微详细但可能更清晰的方法是:
note: the expression 'is_same_v<_Tp, _Up> [with _Tp = void(char, A, C, C, int); _Up = void(char, A, B, C, long int)]' evaluated to 'false'
Run Code Online (Sandbox Code Playgroud)
在这里,我们使用类型推导来形成类型列表C := {Start, Y...}和D := {X..., End},然后对元素类型比较执行合取折叠,在无效链上给出如下错误:
note: the expression '(same_as<C, D> && ...) [with C = {char, A, C, C, int}; D = {char, A, B, C, long int}]' evaluated to 'false'
Run Code Online (Sandbox Code Playgroud)
演示。
| 归档时间: |
|
| 查看次数: |
2218 次 |
| 最近记录: |