Что касается этого вопроса, который, возможно, слишком упрощен, я приведу здесь более сложный пример. Проблема, на которую я претендую, изображена следующим кодом:

// test3.cpp

using namespace std;

template<typename T>
struct exer
{
    template<typename R, typename... rArgs, typename... pArgs>
    R operator()(R(T::*f)(rArgs...), pArgs&&... args)
    {
       return (t.*f)(forward<pArgs>(args)...);
    }

    T t;
};

struct A
{
    int addition() { return 0; }

    template<typename... Args>
    int addition(int a, Args... args) { return a + addition(args...); }
};

struct B
{
public:
    template<typename... Args>
    int addition(Args&&... args)
    {
       return m_e(&A::addition, forward<Args>(args)...);
    }

private:
    exer<A> m_e;
};

int main()
{
    B b;

    cout << b.addition(1, 2, 3, 4) << endl;
}

Проблема здесь в том, что при создании B::addition тип &A::addition неизвестен, поскольку существуют разные перегрузки. Более того, B::addition также не знает, какую перегрузку нужно использовать. Это не знает компилятор, пока функция не будет вызвана. Но для того, чтобы правильно указать, какая перегрузка должна использоваться в exer<A>::operator(), мне нужно выполнить приведение &A::addition, чтобы привести его к правильной перегрузке.

Как мне извлечь тип правильной перегрузки целевой функции?

1
Peregring-lk 19 Мар 2013 в 23:10

1 ответ

Лучший ответ

Измените вопрос. Если вы можете заставить exer принимать вызываемый объект вместо указателя на функцию-член, например:

template<typename T>
struct exer
{
    T t;
    template<typename F, typename... pArgs>
    auto operator()(F f, pArgs&&... args)
    -> decltype(f(t, forward<pArgs>(args)...))
    {
       return f(t, forward<pArgs>(args)...);
    }

};

Тогда вы можете сделать это вместо этого:

struct B
{
public:
    template<typename... Args>
    int addition(Args&&... args)
    {
        struct Invoker {
            auto operator()(A& a, Args&&... args) const
                ->decltype(a.addition(std::forward<Args>(args)...))
            { return a.addition(std::forward<Args>(args)...); }
        };
        return m_e(Invoker(), forward<Args>(args)...);
    }

private:
    exer<A> m_e;
};

Теперь выбор правильного A::addition выполняется компилятором с использованием обычных правил разрешения перегрузки.

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

        return m_e( [](A& a, Args&&... as) {
                      return a.addition(forward<Args>(as)...);
                    },
                    forward<Args>(args)...);
3
Jonathan Wakely 20 Мар 2013 в 00:00