为什么“std::tuple”仍然没有 move-from-elements ctor?

发布于 2025-01-19 22:00:43 字数 1874 浏览 2 评论 0原文

很久以前,我遇到了一个非常像这样的问题:

std::tuple default具有可移动构造元素的构造函数

C++ 正在高速发展和改进,但代码的微小变化仍然让程序员感到困扰。我测试了添加 SFINAE 选择的 ctor 来精确匹配 move-from-elements ctor,然后将问题解决。 现在,闪回让我再次想到这个问题。 std::tuple 仅声明 26 (=13*2) 构造函数。我想知道为什么它仍然缺少一个元素的精确匹配移动?配对<2,15>定义元素的精确匹配副本。这对击败了对<3,16>。重载(通用参考列表中的通用)。对方的举动似乎并不那么困难。随着概念的最终发布,SFINAE 变得更加简单:

template<typename ... Types>
tuple<Types ...>::tuple(Types&&... args)
requires(!(is_lvalue_reference_v<Types>&&...&&true));

template<typename Alloc, typename ... Types>
tuple<Types ...>::tuple( std::allocator_arg_t, const Alloc&, Types&&... args)
requires(!(is_lvalue_reference_v<Types>&&...&&true));

SFINAE 只是防止复制元素对应项的重复定义,以防所有元素都是右值引用;否则 - 如果至少存在一个值或右值引用元素 - 一对新的构造函数将可用。是否还有其他具体原因阻止声明这对构造函数?

下面是举例说明这一点的片段:

struct A{
        A()=default;
        A(A&&){endl(std::cout<<"A(A&&)");};
        A(A const&){endl(std::cout<<"A(A const&)");};
    };
    struct B{
        B()=default;
        B(B&&){endl(std::cout<<"B(B&&)");};
        B(B const&){endl(std::cout<<"B(B const&)");};
    };
    struct C{
        C()=default;
        C(C&&){endl(std::cout<<"C(C&&)");};
        C(C const&){endl(std::cout<<"C(C const&)");};
    };

std::tuple<A,B,C> abc={{},{},{}};

输出是类的复制因子的签名。因为非模板化 ctor#2 优于 ctor#3。现在,如果类的任一复制因子被注释掉(=deleted),则代码将无法编译;因为 ctor#3 应该是显式的。虽然建议的新构造函数应该只是有条件地显式(如果任一元素是显式可移动的)。

Long while ago I faced a question very much like this one:

std::tuple default constructor with move-constructable element

C++ is evolving and improving at a high rate, but minor changes to code still bite a programmers toes. I tested adding a SFINAE opted ctor for exact match move-from-elements ctor and just put the problem away.
Now a flashback took me to the problem again.
std::tuple just declares 26 (=13*2) constructors. I wonder why it is still lacking one for exact match move from elements? The pair <2,15> define exact match copy from elements. This pair defeats the pair<3,16> overloads (generic make from universal reference list). A move from counterpart does not seem that difficult. SFINAE has become a lot simpler with concepts finally shipping:

template<typename ... Types>
tuple<Types ...>::tuple(Types&&... args)
requires(!(is_lvalue_reference_v<Types>&&...&&true));

template<typename Alloc, typename ... Types>
tuple<Types ...>::tuple( std::allocator_arg_t, const Alloc&, Types&&... args)
requires(!(is_lvalue_reference_v<Types>&&...&&true));

The SFINAE just prevents a duplicate definition for copy-from-elements counterparts, in case all elements are rvalue references; otherwise - if at least one value or rvalue reference element exists - a new pair of constructors will be available. Has there been any other specific reason preventing this pair of constructors from being declared?

Here is the snippet that exemplifies the point:

struct A{
        A()=default;
        A(A&&){endl(std::cout<<"A(A&&)");};
        A(A const&){endl(std::cout<<"A(A const&)");};
    };
    struct B{
        B()=default;
        B(B&&){endl(std::cout<<"B(B&&)");};
        B(B const&){endl(std::cout<<"B(B const&)");};
    };
    struct C{
        C()=default;
        C(C&&){endl(std::cout<<"C(C&&)");};
        C(C const&){endl(std::cout<<"C(C const&)");};
    };

std::tuple<A,B,C> abc={{},{},{}};

The output is the signature of copy ctors of the classes. Because none-templated ctor#2 is prefered over ctor#3. Now if either of the copy ctors of classes is commented out(=deleted), the code just fails to compile; because ctor#3 is explicit as it should be. While the proposed new constructors should be only conditionally explicit(if either element is explicitly moveable).

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

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

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文