Има ли начин да се изведе стойността на параметър на шаблон на указател на функция?

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

Възможно ли е да се изведе стойността на параметър на шаблон на указател на функция от аргумент на функция, който е въпросният указател на функция? Например:

using VoidFunction = void(*)();

template <VoidFunction F>
void templ(VoidFunction);

...

void func();  // a VoidFunction

...

templ<func>(func);  // works, but I have to specify the template parameter explicitly
templ(func);        //  <-- I would like to be able to do this

Има ли начин това приспадане да се случи? Изглежда технически възможно от гледна точка на внедряващия компилатор, стига аргументът на функцията да може да бъде преобразуван във функция в кода по време на компилиране.

Ако се чудите за мотивацията зад това, вижте коментарите под този отговор, по-специално възможна оптимизация за прилагането на std::bind().

РЕДАКТИРАНЕ: Осъзнавам, че мога просто да премахна аргумента на функцията и да използвам аргумента на шаблона, както в templ<func>(). Единствената ми цел да добавя аргумента на функцията беше да се опитам да избегна необходимостта от предаване на аргумента на шаблона.

Предполагам, че това, което наистина искам, е също да изведа типа на указателя на функцията, както в:

template <typename Function, Function F>
void templ(/* something */);

и след това да можете да се обадите

templ(func);

or

templ<func>();

и както типът, така и стойността да бъдат изведени от еднократно споменаване на указателя на функцията.

Надявам се, че сега има повече смисъл.


person HighCommander4    schedule 19.07.2013    source източник
comment
Как би могло да го изведе? Например, мога да направя това: template<std::size_t N> void foo(std::size_t i) {int arr[N]; /*fill*/ return arr[i];}. Определено бих искал грешка, ако случайно забравя аргумента на шаблона, когато може да се направи това приспадане.   -  person chris    schedule 19.07.2013
comment
Като се замисля, не съм сигурен, че разбирам защо трябва да се предава и като аргумент на функцията. templ<func>(); не е ли достатъчно?   -  person chris    schedule 19.07.2013
comment
@chris: Добре, въпросът ми нямаше много смисъл, както беше написан :) Моля, вижте моята редакция.   -  person HighCommander4    schedule 19.07.2013
comment
Не мисля, че това е възможно. Имаше предложение да бъде успя да изведе T в template<using typename T, T t>, но не мисля, че успя да влезе.   -  person chris    schedule 19.07.2013
comment
Ако сте доволни от подаването на функцията като аргумент, защо искате същата функция като параметър на шаблон? Какво не е наред с template <typename Function> templ(Function func);?   -  person Igor Tandetnik    schedule 19.07.2013
comment
@IgorTandetnik: За да се позволи на компилатора да извършва по-добри оптимизации чрез генериране на различно инстанциране за всяка функция, предадена като аргумент (не само за всеки тип функция), и след това може би вграждане на всяка, ако е подходящо.   -  person HighCommander4    schedule 19.07.2013


Отговори (1)


Аргументите на шаблона за функция се извеждат от типовете параметри на шаблона на функцията. Аргументите на шаблона могат да бъдат извлечени от тип само когато този тип е една от разрешените форми. Разрешените форми са посочени в [temp.deduct.type]

Аргументите на шаблона могат да бъдат изведени в няколко различни контекста, но във всеки случай тип, който е определен по отношение на параметрите на шаблона (наречете го P), се сравнява с действителен тип (наречете го A) и се прави опит да се намери аргумент на шаблона стойности (тип за параметър на тип, стойност за параметър без тип или шаблон за параметър на шаблон), които ще направят P, след заместване на изведените стойности (наречете го изведен A), съвместим с A.

Аргумент на тип шаблон T, аргумент на шаблон на шаблон TT или аргумент на шаблон без тип i могат да бъдат изведени, ако P и A имат една от следните форми:

T
cv-list T
T*
T&
T[integer-constant]
template-name (where template-name refers to a class template)
type(*)(T)
T(*)()
T(*)(T)
T type::*
type T::*
T T::*
T (type::*)()
type (T::*)()
type (type::*)(T)
type (T::*)(T)
T (type::*)(T)
T (T::*)()
T (T::*)(T)
type[i]
template-name<i> (where template-name refers to a class template)
TT<T>
TT<i>
TT<>

където (T) представлява списъци с аргументи, където поне един тип аргумент съдържа T, а () представлява списъци с аргументи, където нито един параметър не съдържа T. По подобен начин <T> представлява списъци с шаблонни аргументи, където поне един аргумент съдържа T, <i> представлява списъци с шаблонни аргументи, където поне един аргумент съдържа i и <> представлява списъци с шаблонни аргументи, където нито един аргумент не съдържа T или i.

Когато се разглеждат само аргументи на шаблон без тип, съответните форми са тези, които съдържат i:

type[i]
template-name<i> (where template-name refers to a class template)
TT<i>

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

Следният код постига това чрез опаковане на стойността на аргумента на шаблона, който не е тип, в шаблон на клас, наречен NonType. Параметърът на f е във формата template-name<i>, което прави възможно извеждането на стойността на неговия нетипов шаблонен аргумент.

template<typename T, T value>
struct NonType {};

template<typename T, T value>
void f(NonType<T, value>)
{
}

void g();

struct A
{
    void f();
    int m;
};

int i;

#define MAKE_NONTYPE(value) NonType<decltype(value), (value)>()

int main()
{
    f(MAKE_NONTYPE(0)); // NonType<int, 0>
    f(MAKE_NONTYPE(&g)); // NonType<void(*)(), &g>
    f(MAKE_NONTYPE(&A::f)); // NonType<void(A::*)(), &A::f>
    f(MAKE_NONTYPE(&A::m)); // NonType<int A::*, &A::m>
    f(MAKE_NONTYPE(&i)); // NonType<int*, &i>
}

Обърнете внимание, че decltype и макросът MAKE_NON_TYPE се използват тук само за удобство, за да се избегне необходимостта да се изписва пълният списък с аргументи на шаблона на NonType

person willj    schedule 23.07.2013