Как использовать std :: log в качестве функционала? - PullRequest
1 голос
/ 13 февраля 2012

Я пытаюсь передать std :: log в качестве функционального аргумента, но кажется, что есть перегруженные реализации std :: log, и компилятор не смог их разрешить.Коды:

#include <cmath>
#include <iostream>
#include <vector>
#include <string>
#include <functional>

template <typename FOper>
double Eval(FOper fOper, double X)
{
    return fOper(X);
}

int main(int argc, char* argv[])
{
    std::function<double(double)> fPlus1 = std::bind(std::plus<double>(), 1.0, std::placeholders::_1);
    std::cout<<Eval(fPlus1, 10.0)<<std::endl;
    //  how to write this fLog ?
    //std::function<double(double)> fLog = std::log;
    //std::function<double(double)> fLog = std::log<double>;
    std::cout<<Eval(fLog, 10.0)<<std::endl;
    return 0;
}

Компилятор выдает сообщение об ошибке, если я раскомментирую любую строку определения fLog:

error: conversion from '<unresolved overloaded function type>' to non-scalar type 'std::function<double(doubl
e)>' requested

Ответы [ 4 ]

6 голосов
/ 13 февраля 2012

Самый простой способ - просто разыграть его:

typedef double (*log_d)(double);
std::function<double(double)> fLog = static_cast<log_d>(std::log);

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

3 голосов
/ 13 февраля 2012

Как объяснил Xeo, можно заставить его работать, даже если функция перегружена с использованием явного приведения.Однако, поскольку вы уже используете std :: function (которая является функцией C ++ 11), вы также можете просто использовать лямбда-выражение в качестве инициализатора:

function<double(double)> fLog = [](double x){return std::log(x);};

Это предпочтительно в C ++11, потому что это позволяет избежать проблем с перегрузкой.Кроме того, это более эффективно, чем перенос указателя функции, поскольку он сохраняет один уровень косвенности и, следовательно, позволяет встроить лямбда-тело в оператор вызова функции внутреннего объекта-оболочки.

Следует, вероятно, подчеркнуть, что использованиеstd :: function в вашем примере не нужна, поскольку Eval уже является шаблоном функции, а параметр типа FOper может точно соответствовать типу объекта функции без необходимости заключать его в std :: function.Так что, если вам не нужно стирание типа, полученное с помощью std :: function, вы можете написать

template <typename FOper>
double Eval(FOper fOper, double X)
{
    return fOper(X);
}

int main()
{
    auto flog = [](double x){return std::log(x);};
    std::cout << Eval(flog, 10.0) << std::endl;
}
1 голос
/ 13 февраля 2012

Вы делаете это:

typedef double (*logtype)(double);
std::function<double(double)> fLog = (logtype) std::log;

Приведение поможет компилятору выбрать правильную перегрузку.

Вы также можете написать это:

double (*fLog )(double) =  std::log; //i.e don't use std::function

std::cout<<Eval(fLog, 10.0)<<std::endl;
0 голосов
/ 13 февраля 2012

Проблема в том, что нечто подобное само по себе не имеет смысла:

std::bind( std::log, _1 ); // не удается разрешить. привязать какую функцию? Что будет передано _1?

Журнал не является шаблоном, поэтому вы не можете вызвать std::log<double>.

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

template< typename T >
T logT( T t )
{
   return std::log( t );
}

и теперь вы можете использовать logT в вашем коде.

std::bind( logT<double>, _ 1 ) // должно работать.

Конечно, вы можете сделать fLog указателем на функцию logT, если хотите. В C ++ 11 вы можете использовать auto и т. Д., Чтобы не печатать его вручную.

...