有没有一种方法可以使构造函数接受两种可能的参数类型?

时间:2020-09-08 13:50:11

标签: c++ class oop constructor c++17

我有一个MyClass类,目前看起来像这样:

class MyClass{
    public:
        MyClass(std::initializer_list<std::initializer_list<float>> arg){
            // some code here
        }
        MyClass(vector<vector<float>> arg){
            // identical code as above block
        }
        // other class members
};

可以使用MyClass类型和std::initializer_list<std::initializer_list<float>>类型来构造vector<vector<float>>的实例,并且用于处理构造函数参数的代码块是相同的。有没有办法消除代码重复?

2 个答案:

答案 0 :(得分:3)

是否有消除代码重复的方法?

是的。一种简单的方法是仅提供以向量为参数的构造函数(即MyClass(std::vector<std::vector<float>> arg)),并提供额外的括号集以传递向量列表。

class MyClass 
{
   std::vector<std::vector<float>> mStorage;

public:
   MyClass(std::vector<std::vector<float>> arg)
      : mStorage{ std::move(arg) } // use member initializer lists here!
   {}
};

现在您可以

MyClass obj1{ { {1.f, 2.f}, {3.f, 4.f} } }; // note the extra set of {}!
// and
auto vec2D = std::vector<std::vector<float>>{ {1.f, 2.f}, {3.f, 4.f} };
MyClass obj2{ std::move(vec2D) };

See a Demo


但是,如果您坚持保留两者并拥有通用代码,我建议您使用template member function来做到这一点。

class MyClass 
{
   std::vector<std::vector<float>> mStorage;

   template<typename Type>
   void setStorage(Type list) /* noexcept */
   {
      mStorage.assign(std::cbegin(list), std::cend(list));
   }

public:
   MyClass(const std::initializer_list<std::initializer_list<float>> arg) {
      this->setStorage(arg);
   }
   MyClass(std::vector<std::vector<float>> arg) {
      this->setStorage(std::move(arg));
   }
};

现在您可以写

MyClass obj1{ {1.f, 2.f}, {3.f, 4.f} };  // uses std::initializer_list cont'r
// and
auto vec2D = std::vector<std::vector<float>>{ {1.f, 2.f}, {3.f, 4.f} };
MyClass obj2{ std::move(vec2D) }; // uses std::vector cont'r

See a Demo

答案 1 :(得分:2)

我的解决方案:

#include <iostream>
#include <vector>

class MyClass
{
  public:
    using Vec_vec = std::vector<std::vector<float>>;
    MyClass(Vec_vec arg) : vv{std::move(arg)}
    {
        for (const auto& ilist : vv)
        {
            for (const auto& x : ilist)
            {
                s += x;
            }
        }
    }

    MyClass(std::initializer_list<std::vector<float>> arg) : MyClass(Vec_vec(arg.begin(), arg.end())) {}

    Vec_vec vv;
    float s {0};
};

int main()
{
    std::vector<std::vector<float>> v = {{1, 2, 3}, {1, 2, 3}, {4}};
    MyClass a{{1, 2, 3}, {2, 3, 4}, {5}};
    MyClass b{v};
    std::cout << a.s << " " << a.vv[0][0] << "\n";  // 1 + 2 + 3 + 2 + 3 + 4 + 5 = 20
    std::cout << b.s << " " << b.vv[0][0] << "\n";  // 2*(1 + 2 + 3) + 4 = 16
}

注释,按代码中出现的顺序。

  1. using声明只是为了缩短表示法(​​并参数化代码)。与问题无关。
  2. 在第二个构造函数中,我使用向量列表,而不是像您一样使用列表列表。它不仅允许您使用相同的使用模式,而且还应促进优化,例如也可以通过std::move
  3. 这真的很重要:我使用委托的构造函数Delegate Constructor C++https://en.cppreference.com/w/cpp/language/constructor(示例中的第二个构造函数)。在这里,我使用std::vector的构造函数,该构造函数由两个迭代器定义的序列构造向量。该解决方案确实避免了重复代码
  4. 我真的不知道矢量是在这里复制还是移动的。如果性能 对您来说是个问题,并且复制而不是移动它们(在我看来很可能是),则此特殊功能会将向量列表更改为向量向量,并移动向量,应在委托的构造函数中编写并使用,以使编译器满意。
  5. 如果您需要通过值args等传递const reference,我认为您比我更了解,因此这超出了问题的范围,但当然值得考虑。因此,此答案的读者不应将示例中的所有内容都视为“某些”。