17

我正在使用一个模板类,它需要一组整数。代码就像,

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::lambdaboost::spirit),但类型操作的可能性要少得多。

在 Sean Parent 的 boostcon 主题演讲中,他指出仍然不能写pair<int>来表示a pair of integers. 在我的个人库中,我使用了一种语法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

但我相信,还有更多是可能的。

注意:这个问题纯粹是理论上的,我只想知道我可以在 <> 作为类型参数编写的各种语法,而不是关于它的可读性/道德方面,甚至我如何实现我拥有的一些示例给定的,就像工作课一样。

4

2 回答 2

13

可以使用声明符语法构造复合类型 - 可在[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

并非所有组合都会产生有效类型。根据[basic.compound],只能使用以下组合:

可以通过以下方式构造复合类型:

  • 给定类型的对象数组
  • 函数,具有给定类型的参数并返回 void 或给定类型的引用或对象
  • 指向给定类型的 void 或对象或函数(包括类的静态成员)的指针
  • 对给定类型的对象或函数的引用
  • 指向非静态类成员的指针,它们标识给定类的对象中给定类型的成员

提到了其他限制:

[dcl.ptr]没有指向引用的指针

[dcl.ref]不应有对引用的引用,没有引用数组,也没有指向引用的指针

[dcl.mptr]指向成员的指针不应指向...具有引用类型或“cv void”的成员。

[dcl.fct]参数列表(void)相当于空参数列表。除了这种特殊情况,void 不应是参数类型。...如果参数的类型包括“指向 T 的未知边界数组的指针”或“对 T 的未知边界数组的引用”形式的类型,则程序是非良构的。函数不应具有类型数组或函数的返回类型。

一些可能的构造不能用作模板参数。当您显式指定一组模板参数时,编译器必须检查模板参数是否可以替换模板参数而不会导致“无效类型”。根据[temp.deduct]\2,以下构造构成无效类型:

类型扣除可能因以下原因而失败:

  • 尝试创建元素类型为 void、函数类型或引用类型的数组,或尝试创建大小为零或负数的数组。

    template <class T> int f(T[5]);
    int I = f<int>(0);
    int j = f<void>(0); // invalid array
    
  • 试图在限定名称中使用不是类类型的类型。

    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 的引用。

  • 当 T 不是类类型时,尝试创建“指向 T 成员的指针”。

    template <class T> int f(int T::*);
    int i = f<int>(0);
    
  • 试图在模板参数表达式或函数声明中使用的表达式中执行无效转换。

    template <class T, T*> int f(int);
    int i2 = f<int,1>(0); // can’t conv 1 to int*
    
  • 试图创建一个参数类型为 void 的函数类型。

  • 试图创建一个 cv 限定的函数类型。

编辑:这基于 C++03,但也适用于 C++11(它添加了右值引用类型)

于 2013-06-17T12:26:48.703 回答
6

我不完全确定你在问什么。我认为你提供的样本很有趣,并用它玩了一点。

我想出了一个实现,它span<a,b>可以成为 的模板别名indexes<a, ..., b>使用constexpr函数中的类型推导技巧:

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();
}
于 2013-06-17T09:17:56.643 回答