Подтвердить что ты не робот

Функциональные сигнатурные выражения как аргументы шаблона С++

Я смотрел на мини-библиотеку Don Clugston FastDelegate и заметил странный синтаксический трюк со следующей структурой:

TemplateClass< void( int, int ) > Object;

Похоже, что в качестве аргумента для объявления экземпляра шаблона используется сигнатура функции.

Этот метод (присутствие которого в FastDelegate, по-видимому, объясняется одним Jody Hagins), использовался для упрощения объявления экземпляров шаблона с полу-произвольным числом параметров шаблона.

В частности, это позволило сделать следующее:

// A template with one parameter
template<typename _T1>
struct Object1
{
    _T1 m_member1;
};

// A template with two parameters
template<typename _T1, typename _T2>
struct Object2
{
    _T1 m_member1;
    _T2 m_member2;
};

// A forward declaration
template<typename _Signature>
struct Object;

// Some derived types using "function signature"-style template parameters
template<typename _Dummy, typename _T1>
struct Object<_Dummy(_T1)> : public Object1<_T1> {};

template<typename _Dummy, typename _T1, typename _T2>
struct Object<_Dummy(_T1, _T2)> : public Object2<_T1, _T2> {};

// A. "Vanilla" object declarations
Object1<int> IntObjectA;
Object2<int, char> IntCharObjectA;

// B. Nifty, but equivalent, object declarations
typedef void UnusedType;
Object< UnusedType(int) > IntObjectB;
Object< UnusedType(int, char) > IntCharObjectB;

// C. Even niftier, and still equivalent, object declarations
#define DeclareObject( ... ) Object< UnusedType( __VA_ARGS__ ) >
DeclareObject( int ) IntObjectC;
DeclareObject( int, char ) IntCharObjectC;

Несмотря на реальный запах хакерства, я нахожу, что подобная поддельная эмуляция вариационных аргументов шаблона довольно увлекательна.

Реальное мясо этого трюка, похоже, является тем фактом, что я могу передавать текстовые конструкции типа "Type1 (Type2, Type3)" в качестве аргументов шаблонов. Итак, вот мои вопросы: как именно компилятор интерпретирует эту конструкцию? Является ли это сигнатурой функции? Или это просто текстовый шаблон с круглыми скобками? Если первое, то это означает, что любая произвольная сигнатура функции является допустимым типом в отношении процессора шаблонов?

Следующий вопрос будет заключаться в том, что, поскольку приведенный выше пример кода является допустимым кодом, почему стандарт С++ не позволяет вам делать что-то вроде следующего, которое не компилируется?

template<typename _T1>
struct Object
{
    _T1 m_member1;
};

// Note the class identifier is also "Object"
template<typename _T1, typename _T2>
struct Object
{
    _T1 m_member1;
    _T2 m_member2;
};

Object<int> IntObject;
Object<int, char> IntCharObject;
4b9b3361

Ответ 1

Что касается вашего первого вопроса - о типе int(char, float) - это допустимый тип С++ и является типом функции, которая принимает char и float и возвращает int. Обратите внимание, что это тип фактической функции, а не указатель функции, который был бы int (*) (char, float). Фактический тип любой функции - это необычный тип. Например, тип

void DoSomething() {
    /* ... */
}

- void ().

Причина, по которой это во многом не возникает во время обычного программирования, заключается в том, что в большинстве случаев вы не можете объявлять переменные этого типа. Например, этот код является незаконным:

void MyFunction() { 
    void function() = DoSomething; // Error!
}

Однако один случай, когда вы действительно видите используемые типы функций, - это передавать указатели функций вокруг:

void MyFunction(void FunctionArgument()) {
     /* ... */
}

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

Что касается вашего второго вопроса, почему это незаконно иметь тот же шаблон, написанный с разным количеством аргументов, я не знаю точно формулировки в спецификации, которая его запрещает, но это имеет какое-то отношение к тому, что однажды вы объявили шаблон класса, вы не можете изменить количество аргументов. Тем не менее, вы можете предоставить частичную специализацию по этому шаблону, у которого есть другое количество аргументов, при условии, конечно, что частичная специализация специализируется только на исходном количестве аргументов. Например:

template <typename T> class Function;
template <typename Arg, typename Ret> class Function<Ret (Arg)> { 
    /* ... */
};

Здесь Function всегда принимает один параметр. Специализация шаблона принимает два аргумента, но специализация по-прежнему существует только над одним типом (в частности, Ret (Arg)).

Ответ 2

int* int_pointer;    // int_pointer   has type "int*"
int& int_reference;  // int_reference has type "int&"
int  int_value;      // int_value     has type "int"

void (*function_pointer)(int, int);    // function_pointer has type
                                       // "void (*)(int, int)"
void (&function_reference)(int, int);  // function_reference has type
                                       // "void (&)(int ,int)"
void function(int, int);               // function has type
                                       // "void(int, int)"

template<>
struct Object1<void(int, int)>
{
    void m_member1(int, int);  // wait, what?? not a value you can initialize.
};