我正在尝试创建一个模板包装类,该类继承自其模板参数,并一次性覆盖特定基本成员函数的所有重载。下面是一个示例:
#include <cassert>
#include <string>
#include <utility>
template <class T>
class Wrapper: public T {
public:
template <typename... Args>
Wrapper<T>& operator=(Args&&... args) {
return this_member_fn(&T::operator=, std::forward<Args>(args)...);
}
private:
template <typename... Args>
Wrapper<T>& this_member_fn(T& (T::*func)(Args...), Args&&... args) {
(this->*func)(std::forward<Args>(args)...);
return *this;
}
};
int main(int, char**) {
Wrapper<std::string> w;
const std::string s("!!!");
w = s;
assert(w == s);
w = std::string("???");
assert(w == std::string("???"));
return 0;
}
这个想法是,Wrapper<T>::operator=
的模板将根据其参数在编译时选择正确的 T::operator=,然后转发这些参数。如果我用
gcc -std=c++11 -W -Wall -Wextra -pedantic test.cpp -lstdc++
我收到来自 gcc 的以下投诉:
test.cpp: In instantiation of ‘Wrapper<T>& Wrapper<T>::operator=(Args&& ...) [with Args = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >}; T = std::basic_string<char>]’:
test.cpp:26:24: required from here
test.cpp:10:69: error: no matching function for call to ‘Wrapper<std::basic_string<char> >::this_member_fn(<unresolved overloaded function type>, std::basic_string<char>)’
test.cpp:10:69: note: candidate is:
test.cpp:15:15: note: Wrapper<T>& Wrapper<T>::this_member_fn(T& (T::*)(Args ...), Args&& ...) [with Args = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >}; T = std::basic_string<char>]
test.cpp:15:15: note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘std::basic_string<char>& (std::basic_string<char>::*)(std::basic_string<char>)’
test.cpp: In member function ‘Wrapper<T>& Wrapper<T>::operator=(Args&& ...) [with Args = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >}; T = std::basic_string<char>]’:
test.cpp:11:3: warning: control reaches end of non-void function [-Wreturn-type]
第 26 行是w = std::string("???");
,第 15 行是this_member_fn的声明,因此编译器认为func
(= std::string::operator=
) 的类型似乎不是它期望的类型。
有没有办法像我一样使用模板化operator=
来做到这一点,而不是单独覆盖基类中的每个operator=
?
如果您打算当场使用它,则无需获取会员的地址。这也省去了查找要选择哪个重载版本的问题。
template<
typename U
, typename std::enable_if<
std::is_assignable<T&, U>::value
, int
>::type = 0
>
Wrapper& operator=(U&& u)
{
static_cast<T&>(*this) = std::forward<U>(u);
return *this;
}
强烈建议使用约束(通过std::enable_if
进行SFINAE测试),否则尝试将Wrapper<int>
分配给int
Wrapper<int> w, v; w = v;
这样简单的事情将失败。通过约束,将正确选取特殊成员Wrapper& operator=(Wrapper const&);
。
有几个std::string::operator=
,因此表达式&T::operator=
where T = std::string
不会导致指向成员函数的指针。
不,函数模板永远不能是虚拟的,因此不能覆盖任何内容。
但是你可以有一个非虚拟函数模板(所以它不会覆盖任何东西),它可以通过显式限定名称来调用基类函数:
this->T::operator=(std::forward<Args>(args)...);
return *this;
(this->
实际上没有必要,但为了清楚起见而包括在内。
这将通过重载分辨率选择正确的函数,而带有 &T::operator=
的版本不会明确命名单个重载,因此不是单个函数的地址(T::operator=
命名整个重载集,重载集不是C++中的一等对象,因此不能传递给函数。
它不能返回该表达式,因为基类的赋值运算符不返回正确的类型。 您可以使用static_cast<Wrapper&>(...)
但返回*this
更容易。