【问题标题】:Can all overloads of a base member function be overridden and forwarded to by a single templated member function?基成员函数的所有重载都可以被单个模板化成员函数覆盖和转发吗?
【发布时间】:2012-06-25 18:45:20
【问题描述】:

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

#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&lt;T&gt;::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=

【问题讨论】:

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


    【解决方案1】:

    如果您打算在现场使用它,则无需获取会员的地址。这也让您免去了寻找要选择哪个重载版本的问题。

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

    【讨论】:

    • 你不能从 int 继承 :) +1 反正
    【解决方案2】:

    有几个std::string::operator=s,所以表达式&amp;T::operator= where T = std::string 不会导致指向成员函数的指针

    【讨论】:

      【解决方案3】:

      不,function template can never be virtual,所以不能覆盖任何东西。

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

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

      this-&gt; 在此处实际上是不必要的,但为了清楚起见将其包括在内。)

      这将通过重载决议选择正确的函数,而您的带有&amp;T::operator= 的版本并没有明确地命名单个重载,所以不是单个函数的地址(T::operator= 命名整个重载集和重载集不是 C++ 中的一等对象,因此不能传递给函数。)

      它不能返回那个表达式,因为基类的赋值运算符没有返回正确的类型。您可以使用static_cast&lt;Wrapper&amp;&gt;(...),但返回*this 会更容易。

      【讨论】:

      • 按照您的建议替换Wrapper&lt;T&gt;::operator= 的主体确实 工作(事实上,这是我当天早些时候所做的)。但这意味着我不能使用辅助函数也为返回T&amp;的其他成员函数产生重载,例如std::string::assignstd::string::replace,这是我希望做的。
      • 一般来说这是行不通的,因为如果基函数被重载,那么&amp;T::foo 是不明确的。您可以通过例如解决该问题static_cast&lt;T&amp; (*)(const T&amp;)&gt;(&amp;T::operator=) 但这需要提前知道你想要哪个重载,这不会很有帮助。
      猜你喜欢
      • 1970-01-01
      • 2013-09-22
      • 1970-01-01
      • 2010-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多