Шаблон метода-члена, возвращающий экземпляр шаблона класса

Я пытаюсь создать класс-оболочку 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);
}

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

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

0 ответов

Другие вопросы по тегам