我如何制作一个允许所有左值引用、右值引用和初始化器列表的模板化构造函数?

发布于 2024-12-13 13:52:17 字数 1599 浏览 7 评论 0原文

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

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

嗯,它工作得很好,但它没有使用向量的移动构造函数。所以我尝试更改构造函数以接受具有完美转发的右值引用。

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可能无法转换为 std::vector,但它实际上是可转换的,当我尝试不使用enable_if参数。我错过了什么吗?

I'm trying to design a class which has two vectors of large sequences.

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

Well, it works well but it doesn't use the move constructor of vector. So i tried to change the constructor to accept r-value references with perfect forwarding.

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

And now i got a problem. When i try to construct an instance with an initializer_list, i got an error like this.

$ 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&)

I thought that std::initializer_list<double> might not be convertible to std::vector<double>, but it actually is convertible and i got the same error when i tried without the enable_if argument. Am I missing something?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

锦欢 2024-12-20 13:52:17

首选习惯用法是按值传递然后手动移动到成员初始值设定项列表中:

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++ 中收到了一堆警告,所以我不推荐它。只是为了完整性而提及它。

The preferred idiom is to pass by value and then manually move inside the member initializer list:

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

This will work with all possible arguments and be reasonably fast:

  • If you pass a vector lvalue, the vector will be copied into x and then moved into m_x.
  • If you pass a vector rvalue, the vector will be moved into x and then moved again into m_x.
  • If you pass an initializer list, x will be initialized from that list and then moved into m_x.

The alternative is perfect forwarding, but that makes it harder for the client to know what he may pass in:

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

Also, I get a bunch of warnings in g++, so I wouldn't recommend it. Just mentioning it for completeness.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文