类型C ++支持的表达式语法是什么?

时间:2013-06-17 07:12:17

标签: c++ templates c++11

我正在使用一个带有一组整数的模板化类。代码就像,

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::xpressiveboost::lambdaboost::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;作为类型参数,而不是它的可读性/道德方面,甚至我如何实现我给出的一些例子,比如工作类。

2 个答案:

答案 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();
}