23

这个答案列出了 C++17 分解声明的一些缺点(该功能以前称为“结构化绑定”)。例如,您不能为新变量指定显式类型,等等。但是那里没有提到我遇到的一个大缺点,所以我想知道是否有一种我没有想到的已知解决方法。

考虑这个 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};
}

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

我宁愿不使用,因为这需要我在分配之前std::tie(key, p) =给出声明。key这是老生常谈的反对意见std::tie。我可以发誓这就是将结构化绑定引入语言的原因!

那么是否有任何解决方法 - 编写组合构造的任何干净的方式key- 就地和分配给 -p表达我的意图

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

4

3 回答 3

6

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

于 2018-11-14T10:31:26.197 回答
1

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

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

它仍然有限制,“绑定”左值被限制在最后出现,“声明”变量被限制在最先出现,但我认为没有太多方法可以解决这个问题。tuple_shuffle<Is...>如果你需要的话,类似的东西可以处理。

于 2017-03-04T00:41:40.387 回答
1
#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;
    }
于 2021-11-16T18:58:12.833 回答