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

C++17 structured binding that also includes an existing variable

列出了 C++17 分解声明的一些缺点(以前称为 "structured binding" 的功能)。例如,您不能为新变量指定显式类型,等等。但是那里没有提到我 运行 遇到的一大缺点,所以我想知道是否有一个我只是没有想到的已知解决方法。

考虑this JSON-parsing code(可能包含其他错误;出于本问题的目的请忽略它们):

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

这会很好用,除了以 auto [key, p] =auto [value, p] = 开头的行无效!变量 p 已经声明。我正在尝试分配 p 一个新的 ,但我不想创建一个全新的 局部变量

我宁愿不使用std::tie(key, p) =,因为这需要我在赋值前声明key。这是对 std::tie 的熟悉的旧反对意见。我发誓这就是将结构化绑定引入该语言的原因!

那么有没有任何变通方法——任何干净的方式来编写表达我的 意图?

奇怪的是我以前从来没有错过过这个功能,但是一旦你给我结构化绑定来玩,我尝试的第一件事就不管用了。 :(

这是一个非常愚蠢的想法,除非最终没有理智的解决方法,否则我不会认真地建议...但请考虑以下代码。

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

这给了我们

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

它仍然有限制 "tied" 左值被限制出现在 last 和 "declared" 变量被限制出现 first,但我认为没有什么办法可以解决这个问题。如果您需要,tuple_shuffle<Is...> 之类的东西可以处理。

在具有更复杂类型的情况下,可移动临时新对象的简单解决方法可能是朝着您想要的方向迈出的最简单的理想步骤(尽管我认为在您的特定情况下,坚持传统可能更简单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)

很抱歉,我最终无法自己编译它,尽管我认为这是我的 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;

    return 0;
    }