是否可以通过单个模板化成员函数覆盖并转发基本成员函数的所有重载?

时间:2012-06-25 18:45:20

标签: c++ c++11 variadic-templates

我正在尝试创建一个模板包装类,它继承自其模板参数,并且一次性覆盖特定基本成员函数的所有重载。这是一个例子:

#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=

3 个答案:

答案 0 :(得分:4)

如果您打算在现场使用会员,则无需取得会员的地址。这使您无法找到要选择的重载版本。

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> w, v; w = v;分配给Wrapper<int>时,int这样简单的事情就会失败。使用约束,将正确选择特殊成员Wrapper& operator=(Wrapper const&);

答案 1 :(得分:1)

有几个std::string::operator=,因此&T::operator=表达式T = std::string不会导致指向成员函数的指针

答案 2 :(得分:1)

不,function template can never be virtual,因此无法覆盖任何内容。

但是你可以拥有一个非虚函数模板(因此它不会覆盖任何东西),它可以通过显式限定名称来调用基类函数:

this->T::operator=(std::forward<Args>(args)...);
return *this;

this->在那里实际上是不必要的,但为了清楚起见,包括在内。)

这将通过重载分辨率选择正确的函数,而具有&T::operator=的版本不能明确命名单个重载,因此不是单个函数的地址(T::operator=命名整个重载set和重载集不是C ++中的第一类对象,因此不能传递给函数。)

它不能返回该表达式,因为基类的赋值运算符不返回正确的类型。您可以使用static_cast<Wrapper&>(...),但只需返回*this