Шаблон метода-члена, возвращающий экземпляр шаблона класса
Я пытаюсь создать класс-оболочку 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);
}
Поэтому мне удалось найти обходной путь, но у меня есть несколько вопросов:
- Почему мой оригинальный подход вызывает ошибку компилятора?
- Почему мой второй обходной путь решает эту ошибку компилятора?
- Можно ли изменить исходный код для устранения ошибки компилятора без добавления второго параметра шаблона? Если да, то как?