部分填充的模板作为模板模板的参数
Partially filled template as parameter for template template
我有一个带有两个模板参数的模板 (MyCollection) 和另一个模板 (TCTools),需要一个带有一个模板参数的模板作为模板参数。
我定义了一个 "bridge" (TypedCollection) 以从 MyCollection 中获取一个带有一个参数的模板和一个参数作为它的第一个参数,目的是将它传递给模板模板。
如果我使用具有固定类型的桥作为参数,这很好用,但是从另一个模板调用它并使用另一个模板的参数将无法编译。
#include <iostream>
using std::size_t;
template <class Scalar, size_t size>
struct MyCollection
{
MyCollection()
{
std::cout << "Made collection"
<< std::endl
<< " " << __PRETTY_FUNCTION__
<< std::endl;
}
};
template <class Scalar>
struct TypedCollection
{
template <size_t size>
using value = MyCollection<Scalar, size>;
};
template <template <size_t> class TC>
struct TCTools
{
static TC<10> *make_10_sized()
{
return new TC<10>();
}
};
template <class S>
void test()
{
// Will not compile
TCTools<TypedCollection<S>::value>::make_10_sized();
}
int main()
{
// works
TCTools<TypedCollection<int>::value>::make_10_sized();
test<int>();
return 0;
}
GCC 给出了以下注释:
expected a class template, got ‘TypedCollection<S>::value’
整个事情让我很困惑。为什么 test() 中的调用未编译,而 main() 中的调用按预期工作?是否可以让测试工作?
TCTools<TypedCollection<S>::template value>::make_10_sized()
与 typename
非常相似(但不同之处容易混淆),您必须消除 value
的歧义,否则编译器会假定它是一个值而不是类型或模板。
它在 S
被替换之前执行此操作。理论上,TypedCollection<S>
的特化可以使 value
成为任何东西,而且编译器不会尝试猜测。
顺便说一句,如果您最终进行了更多的元编程,您会发现拥有模板、非类型和类型模板参数真的很痛苦。
一种方法是将所有 3 个都变成类型。
template<template<class...>class Z>
struct ztemplate_t {
template<class...Ts> using apply=Z<Ts...>;
};
// C++17
template<auto x>
using zvalue_t = std::integral_constant< std::decay_t<decltype(x)>, x >;
// C++11
template<std::size_t x>
using zsize_t = std::integral_constant< std::size_t, x >;
然后我们可以编写像 apply
这样的模板,将 ztemplate
作为第一个参数并将其应用于其余参数,然后 zapply
即 ztemplate<apply>
.
完成繁重的工作后,TypedCollection<T>
变为 partial_apply_t< zMyCollection_t, T>
。
另一种是将所有 3 个都转换为 constexpr 值并进行 constexpr 值式元编程。
在此之下,我们最终得到 type_t< decltype(zpartial_apply( zMyCollection, tag<T> ))>
。
但是对于小型的一次性图书馆来说,这两种方法都太过分了。
我有一个带有两个模板参数的模板 (MyCollection) 和另一个模板 (TCTools),需要一个带有一个模板参数的模板作为模板参数。
我定义了一个 "bridge" (TypedCollection) 以从 MyCollection 中获取一个带有一个参数的模板和一个参数作为它的第一个参数,目的是将它传递给模板模板。
如果我使用具有固定类型的桥作为参数,这很好用,但是从另一个模板调用它并使用另一个模板的参数将无法编译。
#include <iostream>
using std::size_t;
template <class Scalar, size_t size>
struct MyCollection
{
MyCollection()
{
std::cout << "Made collection"
<< std::endl
<< " " << __PRETTY_FUNCTION__
<< std::endl;
}
};
template <class Scalar>
struct TypedCollection
{
template <size_t size>
using value = MyCollection<Scalar, size>;
};
template <template <size_t> class TC>
struct TCTools
{
static TC<10> *make_10_sized()
{
return new TC<10>();
}
};
template <class S>
void test()
{
// Will not compile
TCTools<TypedCollection<S>::value>::make_10_sized();
}
int main()
{
// works
TCTools<TypedCollection<int>::value>::make_10_sized();
test<int>();
return 0;
}
GCC 给出了以下注释:
expected a class template, got ‘TypedCollection<S>::value’
整个事情让我很困惑。为什么 test() 中的调用未编译,而 main() 中的调用按预期工作?是否可以让测试工作?
TCTools<TypedCollection<S>::template value>::make_10_sized()
与 typename
非常相似(但不同之处容易混淆),您必须消除 value
的歧义,否则编译器会假定它是一个值而不是类型或模板。
它在 S
被替换之前执行此操作。理论上,TypedCollection<S>
的特化可以使 value
成为任何东西,而且编译器不会尝试猜测。
顺便说一句,如果您最终进行了更多的元编程,您会发现拥有模板、非类型和类型模板参数真的很痛苦。
一种方法是将所有 3 个都变成类型。
template<template<class...>class Z>
struct ztemplate_t {
template<class...Ts> using apply=Z<Ts...>;
};
// C++17
template<auto x>
using zvalue_t = std::integral_constant< std::decay_t<decltype(x)>, x >;
// C++11
template<std::size_t x>
using zsize_t = std::integral_constant< std::size_t, x >;
然后我们可以编写像 apply
这样的模板,将 ztemplate
作为第一个参数并将其应用于其余参数,然后 zapply
即 ztemplate<apply>
.
完成繁重的工作后,TypedCollection<T>
变为 partial_apply_t< zMyCollection_t, T>
。
另一种是将所有 3 个都转换为 constexpr 值并进行 constexpr 值式元编程。
在此之下,我们最终得到 type_t< decltype(zpartial_apply( zMyCollection, tag<T> ))>
。
但是对于小型的一次性图书馆来说,这两种方法都太过分了。