51

The code below is from the Boost.Spirit x3 documentation. It uses an interesting C++ syntax that I've never seen before, which is nearly impossible to describe in a search query without knowing the proper terminology. Is this shorthand for the forward declaration of a class? Where is this feature mentioned in the C++ standard?

namespace parser
{
    using x3::eps;
    using x3::lit;
    using x3::_val;
    using x3::_attr;
    using ascii::char_;

    auto set_zero = [&](auto& ctx){ _val(ctx) = 0; };
    auto add1000 = [&](auto& ctx){ _val(ctx) += 1000; };
    auto add = [&](auto& ctx){ _val(ctx) += _attr(ctx); };

    // What is this? This is the very first use of the identifier `roman`.
    x3::rule<class roman, unsigned> const roman = "roman";
    //       ^^^^^^^^^^^

    auto const roman_def =
        eps                 [set_zero]
        >>
        (
            -(+lit('M')     [add1000])
            >>  -hundreds   [add]
            >>  -tens       [add]
            >>  -ones       [add]
        )
    ;

    BOOST_SPIRIT_DEFINE(roman);
}
Barrett Adair
  • 1,306
  • 10
  • 24
  • It may be to disambiguate the class name "roman" from the variable name "roman." – templatetypedef Aug 31 '16 at 00:21
  • Maybe, but it's interesting that the class *doesn't even exist* at the point of the `rule` template instantiation. Maybe it's something that is resolved on the second phase of name lookup? – Barrett Adair Aug 31 '16 at 00:24
  • 9
    To ease your searching: http://en.cppreference.com/w/cpp/language/elaborated_type_specifier – chris Aug 31 '16 at 00:27
  • 1
    @BarrettAdair, That declares the class as well. – chris Aug 31 '16 at 00:29
  • 1
    FWIW, this feature is really handy for tags: `using MyType = TaggedThing;` The tag is useful for making each one a new type, and this is useful for making that type alias one line. Putting `struct` there is my personal preference - it could be `class`, `union`, `enum`, or `enum class` (and variants) AFAIK. – chris Aug 31 '16 at 00:32
  • 5
    Fascinating. I cannot imagine how painful it must be to write a C++ parser. Thank you all! – Barrett Adair Aug 31 '16 at 00:34
  • 1
    There is also a section on elaborated type specifiers here: http://stackoverflow.com/documentation/c%2b%2b/4891/keywords/18504/class#t=201608310036584124696 – Brian Bi Aug 31 '16 at 00:38
  • 1
    As a side note, if you ever want to compile or debug your code, don't use Boost.Spirit. – o11c Sep 07 '16 at 01:21
  • "nearly impossible to describe in a search query without knowing the proper terminology" One of the greatest annoyances of life. – SilentVoid Sep 08 '16 at 00:17

2 Answers2

41

The arguments to a template do not necessarily have to be defined to be used. The use of "class roman" actually declares the class roman.

Here is some example code:

#include <iostream>
template <class T> void foo();
template<> void foo<class roman>()
{
    // allowed because roman is declared
    roman* pointer1;
    // not allowed because romania is not declared
    // romania* pointer2;
    std::cout << "Hello world!" << std::endl;
    return;
}
int main(int argc, char** argv) {
    return 0;
}

As people have pointed out in the comments above, this distinguishes this instantiation of the template. And to directly answer the question you had, specifying the nature of the template argument in a template instantiation is called an 'elaborated type specifier'.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Robert Prévost
  • 1,667
  • 16
  • 22
  • Thank you Robert for the elucidating example. I found my way to the relevant C++ standard sections from the comment from @chris above. In C++11, this is described in ISO/IEC 14882:2011. – Barrett Adair Aug 31 '16 at 01:02
27

It is the same as:

class roman;

x3::rule<roman, unsigned> const roman = "roman";

In other words, writing class T where a typename is expected, first declares that T is the name of a class, and then proceeds with T as the typename being used for the rest of the expression.

Note that in C++ there is no clash between the typename roman and the variable name roman being declared here; that is allowed.


Another case of this can happen without templates, e.g.:

void func( class bar *ptr );

is correct if bar is undeclared; it declares bar and then declares the function to take a pointer to bar.

M.M
  • 138,810
  • 21
  • 208
  • 365