Почему C ++ видит это как неоднозначную ссылку на функцию - PullRequest
6 голосов
/ 16 ноября 2011

Почему мой компилятор может видеть следующий указатель на функцию GetLength неоднозначным
псевдокод:

size_t GetLength(char*);
size_t GetLength(wchar_t*);
struct ITEM { };
double GetLength(ITEM*);

CString GetInfo(ITEM * item, std::function<double (ITEM*)> fn)
{
  ... omitted for clarity
}

ITEM * item = new ITEM;
cout << GetInfo(item, GetLength);  // <- ambiguous error

GetInfo допускает только что-то из шаблона аргумента двойного возврата + ITEM *.Так почему же он рассматривает (а не отбрасывает) две строковые вариации GetLength?

Ответы [ 2 ]

11 голосов
/ 16 ноября 2011

Конструктор для std::function<...> является шаблонным , потому что он должен поддерживать любой тип ввода, похожий на функцию. Не существует единственного типа, который можно попытаться вывести, поэтому все ваши перегрузки можно построить; только позже в компиляции возникнет ошибка для несоответствия типов.

Вы можете сделать это:

GetInfo(item, static_cast<double(*)(ITEM*)>(GetLength));

Чтобы явно отказаться от других перегрузок.


Другими словами, по той же причине это не будет работать:

void foo(int);
void foo(void*);

struct bar
{
    template <typename T>
    bar(T f)
    {
        f(5);
    }
};

bar b(foo);

Хотя тело конструктора для bar будет работать только с void foo(int), он хочет поддерживать любую функцию, в которой f(5) будет работать, поэтому тип аргумента является шаблонным. Это позволяет любой функции работать в этом месте, что означает, что компилятор не может определить единственную наилучшую перегрузку для использования.


Я думаю, что одно решение на уровне языка предназначено для того, чтобы набор перегрузки действительно являлся функтором. То есть дано:

void foo(int);
void foo(void*);

template <typename T>
double foo(int, T);

Именование foo (как в bar(foo) или даже просто foo(5)) приводит к экземпляру этого типа:

struct __foo_overload_set // internal
{
    // forwarders
    void operator()(int __arg0) const
    {
        // where __foo0 is the zeroth overload, etc...
        return __foo0(__arg0);
    }

    void operator()(void* __arg0) const
    {
        return __foo1(__arg0);
    }

    template <typename __Arg1>
    double operator()(int __arg0, __Arg1&& __arg1) const
    {
        return __foo2(__arg0, std::forward<__Arg1>(__arg1));
    }

    // converters
    typedef void(*__foo0_type)(int);

    operator __foo0_type() const
    {
        return __foo0;
    }

    typedef void(*__foo1_type)(void*);

    operator __foo1_type() const
    {
        return __foo1;
    }

    template <typename T>
    struct __foo2_type
    {
        typedef void(*type)(int, T);
    };

    template <typename T>
    operator typename __foo2_type<T>::type() const
    {
        return __foo2;
    }
};

Который, будучи вызванным сам по себе, будет компилироваться в нужном нам контексте. (AFAIK, он не вносит каких-либо двусмысленностей, которые еще не существуют, хотя он полностью не проверен.)

0 голосов
/ 16 ноября 2011

Ваши брекеты не совпадают. Вот почему компилятор не может вас понять

CString GetInfo(ITEM * item, std::function<double ITEM*> fn)
...