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