Есть ли способ вывести значение параметра шаблона указателя функции?

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().

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