我正在使用一个带有一组整数的模板化类。代码就像,
template<unsigned... Idx>
struct work{ ... };
然后我意识到,用户可能需要提供一组整数或一系列整数。所以,我改变了语法,以支持实例化,如
work<indexes<1,3,2,4> > //instead of work<1,3,2,4>
work<span<1,4> > //same as work<1,2,3,4>
虽然在C ++中我们有大量的运算符,可以用来表示异域表达模板(比如boost::xpressive
,boost::lambda
,boost::spirit
等),类型操作的可能性是更不用说了。
在Sean Parent的一个boostcon主题演讲中,他指出仍然无法写pair<int>
来表示a pair of integers
。在我的persinal库中,我创建了一个类似tuple<int[3]>
的语法来表示一个包含3个整数的元组,而不是在类型参数中编写一个带有3个int的元组,并指出我不会将原始数组作为元组参数写入任何地方! (注意:std::array<int,3>
与上面的不一样,因为std :: array无法存储引用,而tuple
可以,std::tuple<int&,int&,int&>
可以说是
所以,我想知道我能写的不同类型的“类型表达式”是什么?
到目前为止,我可以想到对象类型,函数类型,引用类型,有/无cv修饰符,指针等。例如
template<class T>
struct tpl;
using t1 = tpl<int>;//simple type
//function (can have function pointer/reference also)
// e.g. void(*)(int,float) or void(&)(int,float)
using t2 = tpl<void(int,float)>;
//array can have pointer / reference also
//e.g. int(&)[4] or int (*)[4]
using t3 = tpl<int[4]>;
using t4 = tpl<int[]>;
using t5 = tpl<int const>;//with cv modifiers
using t6 = tpl<int*>;//with pointer
using t7 = tpl<int&>;//with reference (& or &&)
using t8 = tpl<tpl<int> >; //template itself
using t9 = tpl<void(...)>; //variadic functions
using t10 = tpl<R C::*>; //pointer to member
但我相信,还有更多可能。
注意:这个问题纯粹是理论上的,我只是想知道我可以在里面写的各种语法&lt;&gt;作为类型参数,而不是它的可读性/道德方面,甚至我如何实现我给出的一些例子,比如工作类。
答案 0 :(得分:12)
可以使用声明符语法构建复合类型 - 可以在[dcl.decl]
中找到。
这种语法的基础是六个基本结构,其中任何T
都可以被列表中的任何其他结构替代。 [在下文中,(T)
表示零个或多个类型的列表(可以以“...”结尾),<T>
表示一个或多个类型的列表。]
T // T
T* // pointer to T
T& // reference to T
T[n] // array of size 'n' of T
T C::* // pointer to C::member of type T
T (T) // function taking '(T)' and returning T
编辑:可以为任何T
替换类模板专精的类型:
C<T> // specialization of class template C with arguments '<T>'
以上组合产生具有特殊意义的构造:
T (*)(T) // pointer to function taking '(T)' and returning T
T (C::*)(T) // pointer to C::member-function taking '(T)' and returning T
此外,一些结构可能是cv合格的:
const T // const T
T* const // const pointer to T
T C::* const // const pointer to C::member of type T
并非所有组合都会产生有效类型。根据{{1}},只能使用以下组合:
化合物类型可以通过以下方式构建:
- 给定类型的对象数组
- 函数,具有给定类型的参数并返回void或给定类型的引用或对象
- 指向void或给定类型的对象或函数(包括类的静态成员)的指针
- 对给定类型的对象或函数的引用
- 指向非静态类成员的指针,这些成员在给定类的对象中标识给定类型的成员
提到了其他限制:
[basic.compound]
没有指向参考的指针
[dcl.ptr]
不应该引用引用,没有引用数组,也没有引用指针
[dcl.ref]
指向成员的指针不应指向......具有引用类型的成员,或 “cv void。”
[dcl.mptr]
参数列表(void)等效于空参数列表。除了这种特殊情况,void不应该是参数类型。 ...如果参数的类型包括“指向未知T的数组的指针”或“引用”的形式类型 对于T的未知界限,“该程序是不正确的。函数不应具有类型数组或函数的返回类型。
某些可能的构造不能用作模板参数。当您显式指定一组模板参数时,编译器必须检查template-arguments是否可以替换模板参数,而不会导致“无效类型”。根据{{1}},以下结构构成无效类型:
由于以下原因,类型扣除可能会失败:
尝试创建一个元素类型为void,函数类型或引用类型的数组,或尝试创建大小为零或负数的数组。
[dcl.fct]
尝试在限定名称中使用非类型的类型。
[temp.deduct]\2
尝试在限定名称的限定符部分中使用类型,该类型在该类型不包含指定成员时命名类型,或者指定的成员不是需要类型的类型。
template <class T> int f(T[5]); int I = f<int>(0); int j = f<void>(0); // invalid array
尝试创建指向引用类型的指针。
尝试创建对引用类型的引用或对void的引用。
当T不是类类型时,尝试创建“指向T成员的指针”。
template <class T> int f(typename T::B*); int i = f<int>(0);
尝试在模板参数表达式或函数声明中使用的表达式中执行无效转换。
template <class T> int f(typename T::B*); struct A {}; struct C { int B; }; int i = f<A>(0); int j = f<C>(0);
尝试创建参数类型为void的函数类型。
- 正在尝试创建符合cv标准的函数类型。
编辑:这是基于C ++ 03,但也适用于C ++ 11(添加rvalue引用类型)
答案 1 :(得分:6)
我不完全确定你在问什么。我认为你提供的样本很有趣并且有点用它。
我想出了一个实现,使span<a,b>
成为indexes<a, ..., b>
的模板别名,使用constexpr
函数中的类型演绎技巧:< / p>
template <int a, int b> using span = decltype(expand_span<a,b>());
现在你可以吃蛋糕了:
////////////////////////////////////////////////////////////////
// using indirect template arguments
template<typename> struct indirect_work { };
void test_indirect()
{
indirect_work<indexes<1,2,3,4>> x;
indirect_work<span<1,4>> y;
x = y; // x and y are of identical types
static_assert(std::is_same<indexes<1,2,3,4>, span<1,4>>::value, "fact check");
}
但是,或许更有趣的是,您仍然可以让主work
模板采用原始<int...>
模板参数列表:
////////////////////////////////////////////////////////////////
// using direct template arguments
template<int...> struct direct_work { };
// deduction alias:
template<int... direct> constexpr direct_work<direct...> deduction_helper(indexes<direct...>);
template <typename Idx> using deduce = decltype(deduction_helper(Idx{}));
void test_direct()
{
direct_work<1,2,3,4> x;
deduce<indexes<1,2,3,4>> y;
deduce<span<1,4>> z;
static_assert(std::is_same<decltype(x), decltype(y)>::value, "fact check");
static_assert(std::is_same<decltype(x), decltype(z)>::value, "fact check");
}
请在此处查看完整的工作演示:gcc on ideone。我在当地用clang编译它。
expand_span
的代码重复此处,以防案例链接失效:
#include <type_traits>
template <int...> struct indexes {};
namespace {
template<int a, int... other>
constexpr indexes<a, other...> combine(indexes<other...> deduce);
template<int a, int b, typename Enable = void> struct expand_span_; // primary
template<int a, int b>
struct expand_span_<a, b, typename std::enable_if< (a==b), void >::type> {
static constexpr indexes<a> dispatch();
};
template<int a, int b>
struct expand_span_<a, b, typename std::enable_if< (a<b), void >::type> {
static constexpr decltype(combine<a>(expand_span_<a+1, b>::dispatch()))
dispatch();
};
template<int a, int b>
constexpr auto expand_span() -> decltype(expand_span_<a,b>::dispatch());
}
template <int a, int b> using span = decltype(expand_span<a,b>());
////////////////////////////////////////////////////////////////
// using indirect template arguments
template<typename> struct indirect_work { };
void test_indirect()
{
indirect_work<indexes<1,2,3,4>> x;
indirect_work<span<1,4>> y;
x = y; // x and y are of identical types
static_assert(std::is_same<indexes<1,2,3,4>, span<1,4>>::value, "fact check");
}
////////////////////////////////////////////////////////////////
// using direct template arguments
template<int...> struct direct_work { };
// deduction alias:
template<int... direct> constexpr direct_work<direct...> deduction_helper(indexes<direct...>);
template <typename Idx> using deduce = decltype(deduction_helper(Idx{}));
void test_direct()
{
direct_work<1,2,3,4> x;
deduce<indexes<1,2,3,4>> y;
deduce<span<1,4>> z;
static_assert(std::is_same<decltype(x), decltype(y)>::value, "fact check");
static_assert(std::is_same<decltype(x), decltype(z)>::value, "fact check");
}
int main()
{
test_indirect();
test_direct();
}