尝试交换可变参数模板 类 时如何正确重载 operator=?

How to properly overload operator= when trying to swap variadic templated classes?

我有一个 class,我在其中取出任意数量的 classes 并将它们存储到 std::tuple 中。它是一个可变参数模板 class。我已经正确地重载了​​ operator+ 运算符,它们按预期工作。但是,现在我正在尝试将一个控制器 class 分配给另一个控制器。我尝试了以下方法:

template<typename...ClassesR>
auto operator=(const Controller<ClassesR...>& rhs)
{
  objects.swap(rhs.getObjects());
  return *this;
}

但是,当我编译代码时,出现错误:“没有匹配函数调用 std::tuple<A&,B&>::swap(std::tuple<A&,B&,A&>)

注意:参数 1 没有从 std::tuple<A&,B&,A&>std::tuple<A&,B&>& 的已知转换"

简而言之,我正在尝试执行以下操作:

ClassA A(3);
ClassB B(3.4);
ClassC C(4.5f);

Controller<ClassA,ClassB> controllerA(A,B);
Controller<ClassC> controllerB(C);

// thinking about it now, this might be the problem...because controllerA is actually
// Controller<ClassA,ClassB> and not Controller<ClassA,ClassB,ClassA>.
controllerA = controllerA + controllerB; 

//or
//controllerA = controllerB;

这是我正在使用的代码:

#ifndef CONTROLLER_HPP
#define CONTROLLER_HPP

#include <functional>
#include <vector>
#include <utility>
#include <any>

template<typename...Classes>
class Controller
{
  public:
    Controller(Classes&...objects) : objects(objects...){ }
    Controller(std::tuple<Classes&...> tup) : objects(tup){ }

    //...a bunch of code that doesn't matter

    std::tuple<Classes&...> getObjects() const
    {
      return objects;
    }        

    template<typename...ClassesR>
    auto operator=(const Controller<ClassesR...>& rhs)
    {

      objects.swap(rhs.getObjects());

      return *this;
    }

  private:
    std::tuple<Classes&...> objects;

};

template<typename...ClassesL, typename...ClassesR>
auto operator+(const Controller<ClassesL...>& lhs, const Controller<ClassesR...>& rhs)
{
  return Controller(std::tuple_cat(lhs.getObjects(),rhs.getObjects()));
}

template<typename...ClassesL, typename ClassesR>
auto operator+(const Controller<ClassesL...> &lhs, ClassesR rhs)
{
  Controller<ClassesR> makeController(rhs);
  return Controller(std::tuple_cat(lhs.getObjects(),makeController.getObjects()));
}

template<typename ClassesL, typename...ClassesR>
auto operator+(ClassesL lhs, const Controller<ClassesR...> &rhs)
{
  Controller<ClassesL> makeController(lhs);
  return Controller(std::tuple_cat(makeController.getObjects(),rhs.getObjects()));
}


#endif // CONTROLLER_HPP

在这种情况下,重载 operator= 的正确方法是什么?正如我所指出的,在我写这篇文章时,可能是因为模板化的 classes 可能是一成不变的。所以 Controller<ClassA,ClassB> 不能修改为 Controller<ClassA,ClassB,ClassA> 所以也许我需要 return 一个新的控制器?

正如在这个问题中看到的:What are the basic rules and idioms for operator overloading?,正确的方法是按值接受对象,而不是按常量引用。所以,例如:

auto& operator=(Controller<ClassesR...> rhs)

正如您所发现的那样(我假设)的部分原因是,如果您经过 const &,您将被迫仅使用 const 函数。 swap() 不是常数函数。

此外,auto 永远不是参考,除非您将其设为参考。因此,要遵循 opertor= 习语,您需要 return a auto&.

More information on this 比我解释得更好。

在 C++ 中,对象永远不能改变它们的类型,class 模板的各种特化是不同的、不相关的类型。考虑一个假设的 std::vector<int*>::operator=(const std::vector<bool>&);很明显,这是一个无意义的请求,默认情况下,限制会延续到 any class 模板,即使它没有类型依赖于模板参数的成员。 (这样做的一个次要原因是允许专业化具有不相关的成员——通过继承或通过显式或部分专业化。)

因此,定义一个接受包含 class 模板的任何特化的赋值运算符 template 通常是没有意义的:人们无法用它做任何事情数据(对于此类模板的大多数专业化)。即使 class 模板是无状态的,通过这样的定义来削弱类型系统通常不是一个好主意。

当然有一些方法可以绕过这些限制,但要在语法和效率方面付出各种代价。一个(小的)静态类型集合(尤其是大小相似的)可以保存在 std::variant<…>(内部是联合或字节缓冲区)中;一组无限的类型可以 保存在 std::any 中(除小对象外,它是指向内部堆分配的类型擦除指针)。