1

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

#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 ‘&lt;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]

第 26w = std::string("???");行是第 15 行是 this_member_fn 的声明,所以编译器认为func(= std::string::operator=) 的类型似乎不是它所期望的类型。

Is there a way to do this using a templated operator= like I am, rather than overriding each operator= in the base class individually?

4

3 回答 3

4

There's no need to take the address of a member if you intend to use it on the spot. This spares you the problem of finding which overloaded version to pick, too.

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;
}

The constraint (the SFINAE test via std::enable_if) is highly recommended as otherwise something as simple as Wrapper<int> w, v; w = v; will fail by attempting to assign a Wrapper<int> to an int. With the constraint, the special member Wrapper& operator=(Wrapper const&); will properly be picked.

于 2012-06-25T19:05:33.140 回答
1

There are several std::string::operator=s, so the expression &T::operator= where T = std::string would not result in a pointer to member function.

于 2012-06-25T18:50:19.927 回答
1

No, a function template can never be virtual, so cannot override anything.

But you can have a non-virtual function template (so it doesn't override anything) and it can call the base class function by explicitly-qualifying the name:

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

(The this-> is actually unnecessary there, but included for clarity.)

This will select the right function by overload resolution, whereas your version with &T::operator= doesn't unambiguously name a single overload, so is not the address of a single function (T::operator= names a whole overload set and overload sets aren't first-class objects in C++ so can't be passed to a function.)

It can't return that expression, because the base class' assignment operator doesn't return the right type. You could use a static_cast<Wrapper&>(...) but it's easier just to return *this.

于 2012-06-25T18:50:51.160 回答