73

In JavaScript ES6, there is a language feature known as destructuring. It exists across many other languages as well.

In JavaScript ES6, it looks like this:

var animal = {
    species: 'dog',
    weight: 23,
    sound: 'woof'
}

//Destructuring
var {species, sound} = animal

//The dog says woof!
console.log('The ' + species + ' says ' + sound + '!')

What can I do in C++ to get a similar syntax and emulate this kind of functionality?

Jan Tojnar
  • 5,306
  • 3
  • 29
  • 49
Trevor Hickey
  • 36,288
  • 32
  • 162
  • 271
  • In C++ you can overload operators. If you define a `structure` and overload its assignment operator accordingly, maybe you can achieve what you are aiming for. Not sure thought. But you could maybe research into that direction. – Ely Jul 13 '15 at 22:26
  • Python and Ruby also allows assignment to tuples, but I think this *Object destructuring* syntax is pretty unique to JS... – Karoly Horvath Jul 13 '15 at 22:30
  • @Karoly: I don't think the syntax is particularly unique; if this is doing what it looks like, `C++` essentially already has this in the form of `using` statements. The novel bit is the extension to allow import members of objects as well as members of namespaces. e.g. if someone said `using animal.species;` was in c++1z, what do you think it would do? –  Jul 14 '15 at 06:01
  • @Hurkyl: this isn't like `using`. It's a *new* variable declaration. – Karoly Horvath Jul 14 '15 at 06:42
  • Finally this is possible in C++17. – Appaji Chintimi Aug 05 '21 at 06:44
  • @AppajiChintimi it is not. Structured bindings are index based. Destructuring is name based. – Sergey Kolesnik Jan 01 '23 at 08:05

4 Answers4

136

In C++17 this is called structured bindings, which allows for the following:

struct animal {
    std::string species;
    int weight;
    std::string sound;
};

int main()
{
  auto pluto = animal { "dog", 23, "woof" };

  auto [ species, weight, sound ] = pluto;

  std::cout << "species=" << species << " weight=" << weight << " sound=" << sound << "\n";
}
dalle
  • 18,057
  • 5
  • 57
  • 81
  • 9
    I believe this is the most up to date and relevant answer. – Alexandre Jul 02 '20 at 09:10
  • 2
    Is there anything equivalent to `std::ignore` when we don't want to destructure everything? – timbo Dec 04 '20 at 02:55
  • 7
    Just a warning that this is not quite equivalent to the JavaScript version. If you swap `weight` and `sound` in the assignment list so `[ species, sound, weight ] = ...`, it prints `weight="woof"` and `sound=23`. – MHebes Oct 15 '21 at 00:30
  • 4
    @MHebes I agree it is similar to the array destructuring in Javascript (which is index based) and not the object destructuring in Javascript (which is name based). – dalle Oct 20 '21 at 08:52
57

For the specific case of std::tuple (or std::pair) objects, C++ offers the std::tie function which looks similar:

std::tuple<int, bool, double> my_obj {1, false, 2.0};
// later on...
int x;
bool y;
double z;
std::tie(x, y, z) = my_obj;
// or, if we don't want all the contents:
std::tie(std::ignore, y, std::ignore) = my_obj;

I am not aware of an approach to the notation exactly as you present it.

Escualo
  • 40,844
  • 23
  • 87
  • 135
  • You can add `make_tie` to `animal` that returns a `tie`. – Yakk - Adam Nevraumont Jul 13 '15 at 23:23
  • The notation is not important. Only the semantics. To be fair, the javascript notation looks a bit weird for destructuring. Most languages use either just the comma operator: `x, y = z` or braces `(x,y) = z` – slebetman Jul 14 '15 at 03:10
  • 1
    @slebetman The JS example destructures an object's properties. Simply using the usual comma-separated would be undefined, seeing object keys have no order. – This company is turning evil. Jul 14 '15 at 03:37
  • 1
    @Kroltan: It depends on the language. In some languages the comma operator is the destructuring operator. What's "usual" in some languages may not be in others. – slebetman Jul 14 '15 at 06:20
  • @slebetman But the point is, Javascript has both notations, only the comma (with square brackets) is for *array elements* and the `{}` delimited is for *object properties*. `[a,b] = [1,2]`, `{a,b}={a:"hello", b:12}`. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment – This company is turning evil. Jul 14 '15 at 14:36
  • @Kroltan: No, that's not the point at all... sigh.. the point is, Escualo is worried that his approach may not meet the OPs requirement because the syntax is different form javascript but syntax is not important because languages other than javascript use syntax different from javascript for destructuring. – slebetman Jul 14 '15 at 14:45
  • @slebetman ...Which extends to the fact that the OP desires to destructure objects, not lists/tuples. – This company is turning evil. Jul 14 '15 at 14:47
  • I admit, my question was a bit open-ended in terms of desired syntax and ease of use. Regardless, destructing is a language concept, and I was curious how that concept could be emulated in a language that does not explicitly have it. I expected varying results and leaving it up to the readers what would be best for their given situation. – Trevor Hickey Jul 14 '15 at 15:28
  • 1
    For `std::tuple`, there's a proposal offering something similar to `auto {x, y, z} = some_tuple;` syntax. It seems to be progressing decently well. – chris Jan 07 '16 at 20:13
5

Mostly there with std::map and std::tie:

#include <iostream>
#include <tuple>
#include <map>
using namespace std;

// an abstact object consisting of key-value pairs
struct thing
{
    std::map<std::string, std::string> kv;
};


int main()
{
    thing animal;
    animal.kv["species"] = "dog";
    animal.kv["sound"] = "woof";

    auto species = std::tie(animal.kv["species"], animal.kv["sound"]);

    std::cout << "The " << std::get<0>(species) << " says " << std::get<1>(species) << '\n';

    return 0;
}
Chad
  • 18,706
  • 4
  • 46
  • 63
1

Another possibility could be done as

#define DESTRUCTURE2(var1, var2, object) var1(object.var1), var2(object.var2)

which would be used like:

struct Example
{
    int foo;
    int bar;
};

Example testObject;

int DESTRUCTURE2(foo, bar, testObject);

yielding local variables of foo and bar.

Of course it's limited to creating variables all of the same type, although I suppose you could use auto to get around that.

And that macro is limited to doing exactly two variables. So you would have to create DESTRUCTURE3, DESTRUCTURE4, and so on to cover as many as you want.

I don't personally like the code style this ends up with, but it comes reasonably close to some of the aspects of the JavaScript feature.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
TheUndeadFish
  • 8,058
  • 1
  • 23
  • 17