Сигнатуроподобные выражения функции как аргументы шаблона C ++ - PullRequest
52 голосов
/ 10 января 2011

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

TemplateClass< void( int, int ) > Object;

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

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

То есть, это позволило что-то вроде следующего:

// 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;

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

Настоящим смыслом этого трюка является тот факт, что я могу передавать текстовые конструкции, такие как «Тип1 (Тип2, Тип3)», в качестве аргументов для шаблонов. Итак, вот мои вопросы: как именно компилятор интерпретирует эту конструкцию? Это сигнатура функции? Или это просто текстовый шаблон с круглыми скобками? Если первое, то означает ли это, что любая сигнатура функции является допустимым типом в том, что касается процессора шаблонов?

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

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;

Ответы [ 2 ]

42 голосов
/ 10 января 2011

Относительно вашего первого вопроса - о типе int(char, float) - это допустимый тип C ++ и тип функции, который принимает 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)).

4 голосов
/ 10 января 2011
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.
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...