简体   繁体   English

还包括现有变量的 C++17 结构化绑定

[英]C++17 structured binding that also includes an existing variable

This SO answer lists some shortcomings of C++17 decomposition declarations (the feature formerly known as "structured binding").这个 SO answer列出了 C++17 分解声明的一些缺点(该功能以前称为“结构化绑定”)。 For example, you can't give explicit types to the new variables, and so on.例如,您不能为新变量指定显式类型,等等。 But one big shortcoming I'm running into isn't mentioned there, so I wonder if there's a known workaround that I'm just not thinking of.但是那里没有提到我遇到的一个大缺点,所以我想知道是否有一种我没有想到的已知解决方法。

Consider this JSON-parsing code (which may contain other bugs; please ignore them for the purposes of this question):考虑这个 JSON 解析代码(可能包含其他错误;出于本问题的目的,请忽略它们):

using Value = std::any;
using String = std::string;
using Object = std::map<String, Value>;

std::pair<String, const char *> load_string(const char *p, const char *end);
std::pair<Value, const char *> load_value(const char *p, const char *end);
const char *skip_spaces(const char *p, const char *end);

std::pair<Object, const char *> load_object(const char *p, const char *end)
{
    p = skip_spaces(p, end);
    if (p == end || *p++ != '{') throw ParseError("Expected {");
    p = skip_spaces(p, end);
    Object result;
    if (p == end && *p == '}') {
        // the object has no key-value pairs at all
    } else {
        while (true) {
            auto [key, p] = load_string(p, end);
            p = skip_spaces(p, end);
            if (p == end || *p++ != ':') throw ParseError("Expected :");
            auto [value, p] = load_value(p, end);
            result.insert_or_assign(std::move(key), std::move(value));
            p = skip_spaces(p, end);
            if (p == end) throw ParseError("Expected , or }");
            if (*p == '}') break;
            if (*p++ != ',') throw ParseError("Expected , or }");
        }
    }
    return {result, p+1};
}

This would work great, except that the lines starting auto [key, p] = and auto [value, p] = are invalid!这会很好用,除了以auto [key, p] =auto [value, p] =开头的行无效! The variable p has already been declared.变量p已被声明。 I'm trying to assign p a new value , but I don't want to create a whole new local variable .我正在尝试为p分配一个新,但我不想创建一个全新的局部变量

I would prefer not to use std::tie(key, p) = , because that requires me to give a declaration for key before the assignment.我宁愿不使用std::tie(key, p) = ,因为这需要我在分配之前给出key的声明。 This is the familiar old objection to std::tie .这是对std::tie的常见反对意见。 Which I could have sworn was the reason structured binding was introduced into the language!我可以发誓这就是将结构化绑定引入语言的原因!

So is there any workaround — any nice clean way of writing the combination construct- key -in-place-and-also-assign-to- p that expresses my intention ?那么是否有任何解决方法 - 编写组合construct- key -in-place-and-also-assign-to- p的任何干净的方式来表达我的意图

It's strange how I never missed this feature before, but as soon as you give me structured binding to play with, the first thing I try doesn't work.奇怪的是,我以前从未错过过这个功能,但是一旦你给我结构化绑定来玩,我尝试的第一件事就不起作用。 :( :(

In cases with more complex types, a simple workaround of a movable temporary new object might be the simplest desirable step in the direction of what you want (though I reckon in your particular case it might be simpler to stick to traditional tie instead):在类型更复杂的情况下,可移动临时新对象的简单解决方法可能是朝着您想要的方向迈出的最简单的理想步骤(尽管我认为在您的特定情况下,坚持传统的tie可能更简单):

... // (as in your code: p & end exist already, key & p_ not yet)

auto [key, p_] = load_string(p, end);
p = move(p_);

... // (continue using p)

I'm sorry I eventually couldn't compile it myself though I could see it being a problem of my IDE (CLion which currently only halfly supports C++17) though I'd expect it to work in general.很抱歉,我最终无法自己编译它,尽管我可以看到它是我的 IDE(CLion 目前仅支持 C++17 的一半)的问题,尽管我希望它能够正常工作。

#include <iostream>
#include <limits>
#include <tuple>

int main()
{
    auto step = std::numeric_limits<double>::infinity();
    auto as = std::numeric_limits<int>::infinity();

    std::tie(step, as) = std::tuple{ 0.1, 2 };
    std::cout << step << ", " << as << std::endl;
}

This is a really dumb idea that I wouldn't seriously suggest unless there ends up being no sane workaround... but consider the following code.这是一个非常愚蠢的想法,除非最终没有合理的解决方法,否则我不会认真建议...但请考虑以下代码。

template<size_t P, size_t... Is>
auto plus(std::index_sequence<Is...>)
{
    return std::index_sequence<P+Is...>{};
}

template<typename RHS, size_t... Is>
auto tuple_select(RHS&& rhs, std::index_sequence<Is...>)
{
    return std::forward_as_tuple(std::get<Is>(std::forward<RHS>(rhs))...);
}

template<typename... Ts>
struct AndTie {
    std::tuple<Ts&...> v;
    AndTie(Ts&... vs) : v(vs...) {}

    template<typename RHS>
    auto operator=(RHS&& rhs) && {
        constexpr int N = std::tuple_size_v<RHS>;
        constexpr int K = sizeof...(Ts);
        v = tuple_select(std::forward<RHS>(rhs), plus<N-K>(std::make_index_sequence<K>{}));
        return tuple_select(std::forward<RHS>(rhs), std::make_index_sequence<N-K>{});
    }
};

This gives us这给了我们

auto [key] =AndTie(p)= load_string(p, end);
auto [value] =AndTie(p)= load_value(p, end);

It still has the limitation that the "tied" lvalues are constrained to appear last and the "declared" variables are constrained to appear first , but I don't think there's much way to get around that.它仍然有限制,“绑定”左值被限制在最后出现,“声明”变量被限制在最先出现,但我认为没有太多方法可以解决这个问题。 And something like tuple_shuffle<Is...> could handle that if you needed it.如果你需要的话,像tuple_shuffle<Is...>这样的东西可以处理。

#include <tuple>
#include <iostream>
#include <limits>   

template <typename ... Ts>
std::tuple<Ts& ...> BindTo (Ts& ... bind)
    {
    return std::tuple<Ts& ...> (bind ...);
    }

int main ()
    {
    auto step = std::numeric_limits<double>::infinity ();
    auto as = std::numeric_limits<double>::infinity ();
    auto en = std::numeric_limits<double>::infinity ();
    BindTo (step, as, en) = std::tuple<double, double, double> { 0.001, 0.01, 0.1 };
    std::cout << step << ", " << as << ", " << en << std::endl;
    }

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

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