Это так ответ перечисляет некоторые недостатки объявлений декомпозиции 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
-в-место-и-и-правопреемник-top
это выражает мое намерение?
Странно, что я никогда не пропускал эту функцию раньше, но как только вы дадите мне структурированное связывание, первое, что я попробую, не получится. 🙁
Это действительно глупая идея, которую я бы не предложил всерьез, если бы в конечном итоге нет нормальный обходной путь … но рассмотрим следующий код.
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...>
может справиться с этим, если вам это нужно.
В случаях с более сложными типами простой обходной путь подвижного временного нового объекта может быть самым простым желательным шагом в направлении того, что вы хотите (хотя я считаю, что в вашем конкретном случае было бы проще придерживаться традиционных 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), хотя я ожидал, что он будет работать в целом.