我如何使一个模板化的构造函数允许所有的l值ref, r值ref和initializer_list

How can i make a templated constructor allowing all of l-value ref, r-value ref and initializer_list?

本文关键字:ref list initializer 许所 何使一 构造函数      更新时间:2023-10-16

我正在尝试设计一个类,它有两个大序列向量。

std::vector<double> factory() {
    return std::vector<double>{1,2,3}; // it actually generates a large sequence of double
}
struct my_class {
    my_class(const std::vector<double>& x, const std::vector<double>& y)
     : m_x(x), m_y(y)
    { }
    std::vector<double> m_x;
    std::vector<double> m_y;
};
int main() {
    my_class c(factory(), factory());
    my_class c2(factory(), {0.5, 1, 1.5});
}

它工作得很好,但是它没有使用vector的move构造函数。因此,我试图改变构造函数,以接受具有完美转发的r值引用。

struct my_class {
    template<typename X, typename Y>
    my_class(X&& x, Y&& y
             , typename std::enable_if<std::is_convertible<X, std::vector<double> >::value &&
                                       std::is_convertible<Y, std::vector<double> >::value>::type * = 0
            )
     : m_x(std::forward<X>(x)), m_y(std::forward<Y>(y))
    { }
    std::vector<double> m_x;
    std::vector<double> m_y;
};

现在我遇到了一个问题。当我尝试用initializer_list构造一个实例时,我得到了这样的错误。

$ g++ -W -Wall -std=gnu++0x a.cpp
a.cpp: In function ‘int main()’:
a.cpp:34:32: error: no matching function for call to ‘my_class::my_class(std::vector<double>, <brace-enclosed initializer list>)’
a.cpp:17:18: note: candidate is: my_class::my_class(const my_class&)

我认为std::initializer_list<double>可能不能转换为std::vector<double>,但它实际上是可转换的,当我没有enable_if参数尝试时,我得到了同样的错误。我错过什么了吗?

首选的习惯用法是按值传递,然后手动在成员初始化列表中移动:

struct my_class {
    my_class(std::vector<double> x, std::vector<double> y)
     : m_x(std::move(x)), m_y(std::move(y))
    { }
    std::vector<double> m_x;
    std::vector<double> m_y;
};

这将适用于所有可能的参数,并且相当快:

  • 如果你传递一个向量左值,向量将被复制到x,然后移动到m_x
  • 如果你传递一个向量右值,向量将移动到x,然后再移动到m_x
  • 如果你传递一个初始化列表,x将从该列表初始化,然后移动到m_x

另一种选择是完全转发,但这使得客户端更难以知道他可能传递的内容:

struct my_class {
    template<typename T, typename U>
    my_class(T&& x, U&& y)
     : m_x(std::forward<T>(x)), m_y(std::forward<U>(y))
    { }
    std::vector<double> m_x;
    std::vector<double> m_y;
};

同样,我在g++中得到了一堆警告,所以我不推荐它。只是为了完整起见。