An attribute is the product of a parser.
Synthesis
Each parser has an exposed attribute.
When you combine parsers in expressions, the resulting attributes will be combined into what is called the synthesized attribute. E.g. int_ >> double_
synthesizes into a tuple of (int, double)¹.
Propagation
When exposed attributes get propagated to the surrounding rule context, a lot of automatic compatibility rules and transformations are possible e.g. like in
qi::rule<It, std::string()> r1 = +qi::char_; // std::vector<char> -> std::string
qi::rule<It, boost::optional<int>()> r2 = qi::int_;
qi::rule<It, map<int, double>() > r3 = (qi::int_ >> qi::double) % ';'; // requires `boost/fusion/adapted/std_pair.hpp`
Bound Attribute References
This is a slightly confusing different meaning of attributes;
The parser API accepts variable numbers of arguments that will receive the resulting values from the parse.
These are known as the "bound attributes". The context will actually refer directly to them. The same compatibility/propagation rules apply as above, making it possible to do this directly:
using namespace qi;
std::map<std::string, int> data;
bool ok = phrase_parse(f, l, 'keyvalues: ' >> (lexeme[+alpha] >> '=' >> int_) % ';', space, data);
Extending
The transformations and compatibility rules can be customized for user-defined types. It's a bit beyond the scope here, but documentation can be found here: docs and [SO] hosts a good number of examples the demonstrates their use.
Links
The documentation lists the types synthesized with each operator/directive.
See also Detecting the parameter types in a Spirit semantic action
¹ technically it could be boost::fusion::vector2<int, double>
but you shouldn't care about this implementation detail; attribute propagation rules hide this detail from you