我正在尝试创建一个包装类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);
}
所以我设法找到一种解决方法,但是我有几个问题: