Перегруженная функция С++ в качестве аргумента шаблона

упрощенная версия моего кода здесь

int foo(int x)
{
  return x;
}

int foo(int x, int y)
{
  return x+y;
}

template<typename unary_func>
int bar(int k, unary_func f)
{
  return f(k);
}

int main()
{
  bar(3, foo);
  return 0;
}

Есть ли способ сообщить компилятору, что я хочу передать как аргумент, является первым `foo '?

Ответ 1

Вы можете указать явный аргумент шаблона:

bar<int(int)>(3, foo);

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

bar(3, static_cast<int(*)(int)>(foo));

или обернуть его в другую функцию (или объект функции), чтобы удалить двусмысленность

bar(3, [](int x){return foo(x);});

Ответ 2

Нет, вы не можете, потому что вы вызываете функцию всегда только с одним аргументом, вам нужен тип с одним аргументом. Вместо этого вы можете использовать шаблон по значению (no typename или class)

Один аргумент:


int foo(int x)
{
    return x;
}

int foo(int x, int y)
{
    return x+y;
}

typedef int (*foo_fcn)(int);

template<foo_fcn unary_func>
int bar(int k)
{
    return unary_func(k);
}

int main()
{
    bar<foo>(3);
    return 0;
}

Два аргумента:


int foo(int x)
{
    return x;
}

int foo(int x, int y)
{
    return x+y;
}

typedef int (*foo_fcn)(int, int);

template<foo_fcn unary_func>
int bar(int k)
{
    return unary_func(k, k);
}

int main()
{
    bar<foo>(3);
    return 0;
}

И


int foo(int x) // first foo
{
    return x;
}

int foo(int x, int y) // second foo
{
    return x+y;
}

typedef int (*foo_fcn)(int);
typedef int (*foo_fcn_2)(int, int);

template<foo_fcn unary_func>
int bar(int k)
{
    return unary_func(k);
}

template<foo_fcn_2 unary_func>
int bar(int a, int b)
{
    return unary_func(a, b);
}


int main()
{
    bar<foo>(3,1); // compiler will choose first foo
    bar<foo>(4); // compiler will choose second foo
    return 0;
}

Ответ 3

Я обрабатываю эту проблему со следующим макросом:

#define LIFT(fname) \
[] (auto&&... args) -> decltype (auto) \
{ \
    return fname (std::forward <decltype (args)> (args)...); \
}

Учитывая ваши определения foo и bar, вы можете сказать

int main()
{
    bar(3, LIFT(foo));
    return 0;
}

и будет выбрана правильная перегрузка. Это использует некоторые особенности С++ 14, а именно общие лямбда и decltype(auto). Если вы используете С++ 11, вы можете получить более или менее тот же эффект с немного большей работой:

#define DECLARE_LIFTABLE(NAME) \
struct NAME##_lifter \
{ \
    template <typename... Args> \
    auto operator () (Args&&... args) -> decltype (NAME (std::forward <Args> (args)...)) \
    { \
        return NAME (std::forward <decltype (args)> (args)...); \
    } \
}
#define LIFT(NAME) (NAME##_lifter {})

DECLARE_LIFTABLE(foo);
int main()
{
    bar(3, LIFT(foo));
    return 0;
}

Если вы используете С++ 98, вы в основном зацикливаетесь на приведение к соответствующему типу указателя функции.

Ответ 4

Да:

bar(3, static_cast<int(*)(int)>(&foo));

или

bar<int(*)(int)>(3, &foo);