Шаблон метода-члена, возвращающий экземпляр шаблона класса - PullRequest
0 голосов
/ 10 февраля 2019

Я пытаюсь создать класс-оболочку Wrapper, который имеет функцию callMethod, которая вызывает метод-член обернутого объекта, но возвращает его возвращаемое значение, заключенное в экземпляр Wrapper.

Мой первый подход не сработал, и я не понимаю, почему.Мне удалось найти обходной путь, который работает, но я не понимаю, почему.Я хотел бы попросить немного прояснить ситуацию с черной магией шаблона c ++ в работе здесь.

#include <string>
#include <iostream>
#include <functional>

template <typename WrappedType>
struct Wrapper
{
    Wrapper(WrappedType value):
        m_value(value)
    {
    }

    template<typename ReturnType>
    Wrapper<ReturnType> callMethod(ReturnType (WrappedType::*method)())
    {
        return std::invoke(method, m_value);
    }

    WrappedType m_value;
};

int main()
{
    Wrapper helloString(std::string("Hello World!"));
    auto frontChar = helloString.callMethod(&std::string::front);
    std::cout << frontChar.m_value;

    return 0;
}

Компиляция приведенного выше кода с помощью gcc 8.2.0 (с параметром -std=c++17) приводит к следующей ошибке компилятора:

prog.cc: In instantiation of 'struct Wrapper<char&>':
prog.cc:25:64:   required from here
prog.cc:14:25: error: forming pointer to reference type 'char&'
     Wrapper<ReturnType> callMethod(ReturnType (WrappedType::*method)())
                         ^~~~~~~~~~

Ошибка говорит о том, что проблема в экземпляре Wrapper<char&> (создание экземпляра происходит, когда вызывается helloString.callMethod(&std::string::front), поскольку тип возвращаемого значения - Wrapper<char&>).Я предполагаю, что ошибка происходит, потому что Wrapper<char&>::callMethod недопустимо (char& не является типом класса, поэтому у него нет методов-членов).Я никогда не использовал Wrapper<char&>::callMethod, хотя почему бы это вызвать ошибку?Методы-члены класса шаблона компилируются, только если используются, верно?

Как и ожидалось, изменение типа возвращаемого значения callMethod устраняет ошибку, так как Wrapper<char&> больше не создается.Однако это не решает исходную проблему.

// works but the return value is not wrapped
template<typename ReturnType>
ReturnType callMethod(ReturnType (WrappedType::*method)())
{
    return std::invoke(method, m_value);
}

Далее я попытался выяснить, почему Wrapper<char&>::callMethod может вызвать проблему, если я не вызываю ее.Может быть, компилятор не может понять, что WrappedType является зависимым именем и проверяется на первом этапе двухфазного поиска?Я даже не знаю, имеет ли это смысл, но я попытался пойти с этим.

Я искал зависимые имена и пытался использовать typename и template безрезультатно (я не мог понять, гдеположи их).Затем я попытался добавить второй параметр шаблона в callMethod, который по умолчанию равен WrappedType и, наконец, скомпилирован, но я понятия не имею, почему.Может быть, потому, что компилятор теперь видит его как зависимое имя и проверяет только указатель на функцию-член во второй фазе поиска?

// compiles and works fine
template<typename ReturnType, typename ClassType = WrappedType>
Wrapper<ReturnType> callMethod(ReturnType (ClassType::*method)())
{
    return std::invoke(method, m_value);
}

Итак, мне удалось найти обходной путь, но у меня есть несколько вопросов:

  • Почему мой оригинальный подход вызывает ошибку компилятора?
  • Почему мой второй обходной путь устраняет эту ошибку компилятора?
  • Можно ли изменить исходный код нарешить ошибку компилятора без добавления второго параметра шаблона?Если да, то как?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...