2012-06-13 2 views
8

Yay, другое название вопроса, состоящее из случайной последовательности терминов на C++!Templated conversion function to function pointer

Обычно мы делаем класс Callable путем внедрения operator(). Но вы также можете сделать это, реализуя определяемое пользователем преобразование в указатель функции или ссылочный тип. Вместо использования идеальной пересылки функция преобразования может возвращать указатель на функцию, которая затем вызывается с исходным списком аргументов.

struct call_printf { 
    typedef int printf_t(char const *, ...); 
    operator printf_t &() { return std::printf; } 
}; 

http://ideone.com/kqrJz

Насколько я могу судить, typedef выше синтаксический необходимость. Название функции преобразования формируется из спецификатора типа -seq, что не позволяет построить такую ​​конструкцию, как int (*)(). Для этого потребуется аннотация-декларатор . Предположительно, причина в том, что такие имена типов усложняются, а сложные конструкции, используемые в качестве имен объектов, трудно анализировать.

Функции преобразования также допускаются для шаблонов, но аргументы шаблона должны быть выведены, потому что их явно не указывать. (Это было бы победить весь смысл неявного преобразования.)


Вопрос № 1: В C++ 03, есть не было никакого способа, чтобы указать шаблон оператора преобразования функции? По-видимому, не было способа разрешить аргументы шаблона (т. Е. Назвать их в выведенном контексте) в допустимом типе указателя функции.

Вот эквивалентная ссылка из C++ 11, §13.3.1.1.2/2 [over.call.object]. Это, по существу, то же самое от C++ 03:

Кроме того, для каждой функции без явного преобразования, объявленного в Т вида

operator conversion-type-id() cv-qualifier attribute-specifier-seqopt; 

где CV-классификатор является тем же самым CV- квалификация, или более высокая cv-квалификация, чем, cv, и где Тип преобразования-тип-id обозначает тип «указатель на функцию возврата (P1, ..., Pn), возвращающий R», или тип "Ссылка на указатель на функцию (P1, ..., P п) возвращение R», или типа„ссылка на функцию (P1, ..., Pn) возвращение R“, суррогатную функцию вызова с вызовом-функцией уникального имени и имеющего форму

R call-function (conversion-type-id F, P1 a1, ... ,Pn an) { return F (a1,... ,an); } 

является также рассматривается как кандидатская функция. Аналогично, суррогатные функции вызова добавляются к набору функций-кандидатов для каждой неявной функции преобразования, объявленной в базовом классе T, если функция не скрыта внутри T другой промежуточной декларацией.


Вопрос № 2: В C++ 11, может такое преобразование можно задать с помощью шаблона по умолчанию аргумент в? Это полезно для SFINAE. Единственное отличие здесь от приведенного выше примера состоит в том, что идентификатор типа типа представляет собой описание функции после создания экземпляра, поскольку он является зависимым типом (несмотря на инвариантность).Это отключает GCC и пропускает шаблон участника.

enum { call_alternate = true; } 

struct call_switch { 
    template< bool en = call_alternate > 
    operator typename std::enable_if< en, decltype(fn_1) & >::type() 
     { return fn_1; } 

    template< bool en = ! call_alternate > 
    operator typename std::enable_if< en, decltype(fn_2) & >::type() 
     { return fn_2; } 
}; 

У нас также есть шаблоны с псевдонимами. Кажется, что подстановкой псевдонимов предшествует инстанцирование, учитывая пример в §14.5.7/2, где конфликтуют объявления process. В GCC 4.7 этот код по крайней мере создает экземпляр декларации, но затем он вызывает странный «кандидат ожидает 2 аргумента, 2 предоставленных» ошибки.

template< typename t > 
using fn_t = void (&)(t); 

struct talk { 
    template< typename t > 
    operator fn_t<t>() { return fn; } 
}; 

int main() { 
    talk()(3); 
} 
+7

Мое добро. –

+0

Что вы пытаетесь сделать? Где это может быть полезно? – Nawaz

+0

@Nawaz, когда вы хотите, чтобы оператор преобразования отображал типы указателей? –

ответ

3

Вопрос № 1: В C++ 03, есть не было никакого способа, чтобы указать шаблон оператора преобразования функции? По-видимому, не было способа разрешить аргументы шаблона (т. Е. Назвать их в выведенном контексте) в допустимом типе указателя функции.

Да, это правильно.

Вопрос №2: В C++ 11 можно указать такое преобразование с использованием аргумента шаблона по умолчанию?

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

У нас также есть шаблоны с псевдонимами. Кажется, что замена псевдонима происходит до создания экземпляра, с учетом примера в §14.5.7/2, где объявления конфликта процессов. В GCC 4.7 этот код по крайней мере создает экземпляр декларации, но затем он вызывает странный «кандидат ожидает 2 аргумента, 2 предоставленных» ошибки.

Да, это https://groups.google.com/forum/?fromgroups#!topic/comp.std.c++/lXLFBcF_m3c (и вызвал закрытие DR395), но даже если такой шаблон функции преобразования может работать в тех случаях, как void(&p)() = yourClassObject, он не будет работать для суррогатных вызова функций, поскольку потребность функции преобразования для обеспечения фиксированного не зависящего типа, который преобразуется в объект класса, когда вызывается суррогатная функция, но шаблон функции преобразования не обеспечивает такой тип обычно (такие странные вещи, как template<typename = int> operator Identity<void(*)()>(); в сторону ...).

Я думаю, что GCC может неправильно создает кандидат call-function(void (&)(t), t) с зависимыми типами еще там и попытаться назвать этот кандидат, нарушив тем самым некоторый инвариант него (что может объяснить сообщение странных ошибок - возможно, поражая } else { ... } неожиданно где-то).

+0

Отличный ответ, кроме «должно быть ясно» почему? Формулировка «функция преобразования, объявленная в T формы' operator' * conversion-type-id * '()' ", не говорит о том, что она не является шаблоном. – Potatoswatter

+0

@Potatoswatter Я думаю, что это упущение в спецификации. Но мне нет смысла рассматривать шаблоны функций преобразования здесь. Какова должна быть семантика? –

+0

Пока тип функции может быть выведен из списка аргументов с использованием разрешения перегрузки, тогда не требуется специальной семантики.Это то, что делает GCC (на полпути). По сути, функция суррогатного вызова становится шаблоном функции суррогатного вызова. – Potatoswatter