Каков синтаксис выражения над типами С++? - программирование
Подтвердить что ты не робот

Каков синтаксис выражения над типами С++?

Я работал с шаблоном, который принимает набор целых чисел. Код был похож,

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> 

В то время как в С++ у нас есть большое количество операторов, и их можно использовать для формулировки шаблонов экзотических выражений (например, boost::xpressive, boost::lambda, boost::spirit и т.д.), возможности манипуляции типа намного меньше.

В лейбле boostcon от Sean Parent он отметил, что еще не может написать pair<int> для обозначения a pair of integers. В моей персистентной библиотеке я сделал такой синтаксис, как tuple<int[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

Но я верю, что многое другое возможно.

ПРИМЕЧАНИЕ. Этот вопрос является чисто теоретическим, Я просто хочу знать все виды синтаксиса, которые я могу написать внутри < > как аргумент типа, а не о его аспекте читабельности/нравственности, или даже как я могу реализовать некоторые из приведенных мной примеров, например класс работы.

4b9b3361

Ответ 1

Составные типы могут быть построены с использованием синтаксиса декларатора - найдены в [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

EDIT: тип специализации шаблона шаблона может быть заменен для любого 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 или ссылки или объекты определенного типа
  • указывает на пустоту или объекты или функции (включая статические члены классов) данного типа
  • ссылки на объекты или функции заданного типа
  • указывает на нестатические члены класса, которые идентифицируют элементы заданного типа в объектах данного класса

Указаны дополнительные ограничения:

[dcl.ptr] нет указателей на ссылки

[dcl.ref] Не должно быть ссылок на ссылки, нет массивов ссылок и ссылок на ссылки

[dcl.mptr] Указатель на член не должен указывать на... член со ссылочным типом или "cv void."

[dcl.fct] Список параметров (void) эквивалентен списку пустых параметров. За исключением этого специального случая, void не должен быть типом параметра.... Если тип параметра включает тип формы "указатель на массив неизвестной границы T" или "ссылка к массиву неизвестной границы T", программа плохо сформирована. Функции не должны иметь тип возвращаемого типа массива или функции.

Некоторые из возможных конструкций не могут использоваться в качестве шаблонов-аргументов. Когда вы явно указываете набор аргументов-шаблонов, компилятор должен проверить, что аргументы-шаблоны могут быть заменены шаблонами-параметрами, не приводя к "недопустимому типу". Согласно [temp.deduct]\2, следующие конструкции представляют собой недопустимые типы:

Вывод типа может завершиться неудачно по следующим причинам:

  • Попытка создать массив с типом элемента, который является недействительным, типом функции или ссылочным типом или попыткой создать массив с нулевым или отрицательным размером.

    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.

РЕДАКТИРОВАТЬ: Это основано на С++ 03, но также применяется к С++ 11 (который добавляет типы ссылок rvalue)

Ответ 2

Я не совсем уверен, о чем вы спрашиваете. Я думал, что образец, который вы дали, был интересным и немного поиграл с ним.

Я придумал реализацию, которая делает 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();
}