简体   繁体   English

您如何在 C++ 概念中定义 emplace_back 和其他可变参数模板函数?

[英]How do you define emplace_back and other variadic template functions in a C++ concept?

I'm trying to define a C++ concept for standard library containers that allow push_back/emplace_back:我正在尝试为允许 push_back/emplace_back 的标准库容器定义 C++ 概念:

template <class ContainerType>
concept PushBackContainer = requires(ContainerType a)
{
    requires SequenceContainer<ContainerType>;
    { a.push_back(typename ContainerType::const_reference& v) };
    { a.push_back(typename ContainerType::value_type&& v) };
    // How do you define a variable templated function: 
    { template< class... Args > a.emplace_back(Args&&... args) };
}

The problem I have is how do I define emplace_back with its variadic template arguments?我的问题是如何使用可变参数模板参数定义 emplace_back ? I'm using Visual Studio 2019 but if this isn't supported I'd be interested in the correct syntax come the time it is.我正在使用 Visual Studio 2019,但如果它不受支持,我会对正确的语法感兴趣。

Probably about the best that's worth doing is just a.emplace_back();可能最值得做的就是a.emplace_back(); . .

Your push_back requirements don't have a correct syntax, either.您的push_back要求也没有正确的语法。 I think you want:我想你想要:

template <class ContainerType>
concept PushBackContainer = requires(
    ContainerType& a,
    typename ContainerType::value_type const& cv,
    typename ContainerType::value_type& v)
{
    requires SequenceContainer<ContainerType>;
    a.push_back(cv);
    a.push_back(std::move(v));
    a.emplace_back();
};

Requirements don't check for a function signature;需求不检查函数签名; they check for the validity of an expression (without instantiating more templates than necessary).他们检查表达式的有效性(无需实例化更多模板)。 If we had a class like:如果我们有一个类:

class StrangeContainer {
public:
    using value_type = std::string;
    using const_reference = const value_type&;
private:
    struct ValueHolder {
        ValueHolder(const std::string& s) : value(s) {}
        ValueHolder(std::string&& s) : value(std::move(s)) {}
        std::string value;
    };
public:
    void push_back(ValueHolder);

    template <typename ... Args>
    void emplace_back(Args&&...);
};

then ignoring SequenceContainer requirements, PushBackContainer<StrangeContainer> would be true, and it would also satisfy the Standard's own requirements related to push_back .然后忽略SequenceContainer要求, PushBackContainer<StrangeContainer>将是 true,并且它也将满足与push_back相关的标准自己的要求。 It satisfies the technical requirements, even though it has some surprising effects like the fact that push_back("") is ill-formed.它满足技术要求,即使它有一些令人惊讶的效果,例如push_back("")

So for push_back , we're really just checking that it can be called with a const lvalue and with a non- const rvalue.所以对于push_back ,我们实际上只是检查它是否可以使用const左值和非const右值调用。 (The Standard actually also requires that it can be called with a non- const lvalue and with a const rvalue, and these cases have the same behavior as when called with a const lvalue.) (标准实际上还要求可以使用非const左值和const右值调用它,这些情况与使用const左值调用时具有相同的行为。)

(If you really wanted to test for an exact push_back signature, you could try static_cast<void (ContainerType::*)(typename ContainerType::value_type&&)>(&ContainerType::push_back); - but this is not recommended, since member functions in namespace std are not required to have signatures exactly as described, only to be callable with the same arguments as if declared as described.) (如果你真的想测试一个确切的push_back签名,你可以尝试static_cast<void (ContainerType::*)(typename ContainerType::value_type&&)>(&ContainerType::push_back); - 但不推荐这样做,因为成员函数命名空间std中的签名不需要与所描述的完全相同,只需可以使用与所描述的声明相同的参数调用。)

Also, the standard container class templates don't have any constraints on their push_back or emplace_back functions.此外,标准容器类模板对其push_backemplace_back函数没有任何限制。 Every instantiation of the templates which have push_back declares both overloads, whether or not the type is copyable and/or movable.具有push_back的模板的每个实例都声明了两个重载,无论类型是否可复制和/或可移动。 If not, it would be an error to actually call or otherwise odr-use the push_back function, but it "exists" for purposes of requires-expressions and SFINAE contexts.如果没有,实际调用或以其他方式使用push_back函数将是一个错误,但它“存在”是为了需要表达式和 SFINAE 上下文。 Likewise, the emplace_back member template is declared to accept any number of arguments with any types and value categories, no matter whether they can be used as value_type constructor arguments or not.同样, emplace_back成员模板被声明为接受任意数量的具有任何类型和值类别的参数,无论它们是否可以用作value_type构造函数参数。

So what we would want to test to find out if the container has an emplace_back with an essentially ordinary variadic function declaration would need to be phrased as: Can emplace_back be called with any number of arguments, with each having any possible type and each being either an lvalue or rvalue?因此,我们想要测试以找出容器是否有一个带有本质上普通可变参数函数声明的emplace_back需要表述为: 可以使用任意数量的参数调用emplace_back ,每个参数都有任何可能的类型,每个参数都可以是左值还是右值? I don't think there's any way to really answer that within C++, using requires-expressions, SFINAE tricks, or otherwise.我认为没有任何方法可以在 C++ 中真正回答这个问题,使用需要表达式、SFINAE 技巧或其他方式。 So I would just do one simple test for existence of some sort of emplace_back , and that test might as well be as simple as possible: zero arguments.因此,我只会对某种emplace_back存在进行一个简单的测试,而该测试也可能尽可能简单:零参数。

You could get fancier and also test for some additional cases: Does emplace_back accept different numbers of arguments, up to some fixed maximum?您可以变得更emplace_back ,还可以测试一些其他情况: emplace_back是否接受不同数量的参数,最多达到某个固定的最大值? Does it accept lvalue and rvalue arguments?它接受左值和右值参数吗? Does it accept arguments of dummy struct types?它是否接受虚拟struct类型的参数? Dummy struct types that aren't MoveConstructible?不是 MoveConstructible 的虚拟struct类型? const , volatile , and const volatile types? constvolatileconst volatile类型? All possible combinations of all of the above?以上所有可能的组合? But since you'll never cover all the cases, how much value does each partial enhancement like this really give, compared to the effort, complexity, and maintenance needed to add checks?但是,由于您永远不会涵盖所有情况,与添加检查所需的工作量、复杂性和维护相比,像这样的每个部分增强真正提供了多少价值?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM