What are the expression syntax over types C++ support?

前端 未结 2 925
傲寒
傲寒 2020-12-29 07:41

I was working with a templated class which takes a set of integers. The code was like,

template
struct work{ ... };

相关标签:
2条回答
  • 2020-12-29 08:17

    I'm not entirely sure what you are asking about. I thought the sample you gave was interesting and played a little bit with it.

    I came up with an implementation that makes span<a,b> to be a template alias for indexes<a, ..., b>, using the trick of type deduction in constexpr functions:

    template <int a, int b> using span = decltype(expand_span<a,b>());
    

    Now you can have your cake and eat it:

    ////////////////////////////////////////////////////////////////
    // 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");
    }
    

    But, perhaps more interestingly, you can still have your primary work template take a raw <int...> template argument list:

    ////////////////////////////////////////////////////////////////
    // 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");
    }
    

    See a complete working demonstration here: gcc on ideone. I compiled it with clang locally.


    Full code

    Code for expand_span duplicated here in case link should go dead:

    #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();
    }
    
    0 讨论(0)
  • 2020-12-29 08:18

    Compound types can be constructed using the declarator syntax - found in [dcl.decl].

    Underlying this syntax are six fundamental constructs, within which any T can be substituted by any of the other constructs in the list. [In the following, (T) represents a list of zero or more types (which may end in '...'), and <T> represents a list of one or more types.]

    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
    

    EDIT: The type of a class template specialization can be subsituted for any T:

    C<T> // specialization of class template C with arguments '<T>'
    

    There are combinations of the above that produce constructs which have special significance:

    T (*)(T) // pointer to function taking '(T)' and returning T
    T (C::*)(T) // pointer to C::member-function taking '(T)' and returning T
    

    Additionally, some of the constructs may be cv-qualified:

    const T // const T
    T* const // const pointer to T
    T C::* const // const pointer to C::member of type T
    

    Not all of the combinations result in valid types. According to [basic.compound], only the following combinations can be used:

    Compound types can be constructed in the following ways:

    • arrays of objects of a given type
    • functions, which have parameters of given types and return void or references or objects of a given type
    • pointers to void or objects or functions (including static members of classes) of a given type
    • references to objects or functions of a given type
    • pointers to non-static class members, which identify members of a given type within objects of a given class

    Additional restrictions are mentioned:

    [dcl.ptr] there are no pointers to references

    [dcl.ref] There shall be no references to references, no arrays of references, and no pointers to references

    [dcl.mptr] A pointer to member shall not point to ... a member with reference type, or "cv void."

    [dcl.fct] The parameter list (void) is equivalent to the empty parameter list. Except for this special case, void shall not be a parameter type. ... If the type of a parameter includes a type of the form “pointer to array of unknown bound of T” or “reference to array of unknown bound of T,” the program is ill-formed. Functions shall not have a return type of type array or function.

    Some of the possible constructs cannot be used as template-arguments. When you explicitly specify a set of template-arguments, the compiler must check that the template-arguments can be substituted for the template-parameters without resulting in an 'invalid type'. According to [temp.deduct]\2, the following constructs constitute invalid types:

    Type deduction may fail for the following reasons:

    • Attempting to create an array with an element type that is void, a function type, or a reference type, or attempting to create an array with a size that is zero or negative.

      template <class T> int f(T[5]);
      int I = f<int>(0);
      int j = f<void>(0); // invalid array
      
    • Attempting to use a type that is not a class type in a qualified name.

      template <class T> int f(typename T::B*);
      int i = f<int>(0);
      
    • Attempting to use a type in the qualifier portion of a qualified name that names a type when that type does not contain the specified member, or if the specified member is not a type where a type is required.

      template <class T> int f(typename T::B*);
      struct A {};
      struct C { int B; };
      int i = f<A>(0);
      int j = f<C>(0);
      
    • Attempting to create a pointer to reference type.

    • Attempting to create a reference to a reference type or a reference to void.

    • Attempting to create "pointer to member of T" when T is not a class type.

      template <class T> int f(int T::*);
      int i = f<int>(0);
      
    • Attempting to perform an invalid conversion in either a template argument expression, or an expression used in the function declaration.

      template <class T, T*> int f(int);
      int i2 = f<int,1>(0); // can’t conv 1 to int*
      
    • Attempting to create a function type in which a parameter has a type of void.

    • Attempting to create a cv-qualified function type.

    EDIT: This is based on C++03, but also applies to C++11 (which adds rvalue reference types)

    0 讨论(0)
提交回复
热议问题