14

Sorry for the funny title.

Prior to C++0x, there are restrictions in the use of function-local structs (“local types”) as template arguments. My question is essentially if similar restrictions apply to anonymous structs. Specifically, in the context of a trait class:

template <typename T>
struct trait;

template <>
struct trait<int> {
    typedef int type;
};

template <typename T>
struct trait<std::basic_string<T> > {
    typedef struct {
        T value;
    } type;
};

trait<std::string>::type foo; // Is this valid?

template <typename T>
void f() { }

f<trait<std::string>::type> >(); // Is this?

template <typename T>
void g() { f<typename trait<T>::type>(); }

g<std::string>(); // And this?

Is this valid and reliable? It compiles in recent versions of GCC and LLVM but I’m still insecure whether this is strictly valid, and whether it’s understood by VC++ and ICC.

Community
  • 1
  • 1
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Since the type in question is not used as a template argument I'm not sure how this relates to the restrictions on local types. – Luc Danton May 09 '11 at 16:07
  • @Luc I’ll add an example. But the question stands on its own. Is the above valid? And if so, is it valid with template arguments? – Konrad Rudolph May 09 '11 at 16:11
  • now I am wondering what happens if you create an instance of an anonymous structure and use it as a parameter. (You could get its type using `decltype`) – Matthieu M. May 09 '11 at 16:16
  • `f::type> >();` is SURELY not valid :D. ( there is an extra `>` ) – Nawaz May 09 '11 at 16:20
  • 2
    see http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#62 (of course, the bullet-list is completely gone in C++0x. But note the entry they added during that time for clarity about the name for linkage purposes). – Johannes Schaub - litb May 09 '11 at 20:06

4 Answers4

6

For reference, the quote from the linked question in 14.3.1/2:

A local type, a type with no linkage, an unnamed type or a type compounded from any of these types shall not be used as a template argument for a template type parameter.

My interpretation is that the typedef struct is creating an alias to an unnamed type and that it thus can't be used as a template type parameter. Further note that additionally in C typedef struct {} Foo; is treated rather differently from struct Foo {}; giving precedent that the two forms are not equivalent (although admittedly that difference doesn't appear in C++).

Thus it would appear your first example works (since it's not using the unnamed type as a template type parameter), while the second and third examples would be technically invalid (since they do use it as a template type parameter).

Finally in closing I have to ask, is there a reason you can't name the struct instead of typedefing it?

EDIT: From 7.1.3/1:

...A typedef-name is thus a synonym for another type. A typedef-name does not introduce a new type the way a class declaration (9.1) or enum declaration does...

This strongly implies that using typedef in such a way does not introduce a type suitable for use as a template type-parameter.

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • “is there a reason you can't name the struct” … rather a lot (!) of redundant typing. But other than that, no. So I’ll err on the side of safety and standard compliance. – Konrad Rudolph May 09 '11 at 20:46
  • 1
    @Konrad : Erm, isn't `typedef struct {} type;` more redundant typing than `struct type {};`? Or am I missing something? – Xeo May 09 '11 at 23:51
  • @Xeo Did you just call me stupid in public? :-p Damn, sometimes you don’t see the wood for the trees. – Konrad Rudolph May 10 '11 at 07:00
4

In the upcoming standard that restriction is removed from the language. The standard says in

14.3.1 [temp.arg.type] /1

A template-argument for a template-parameter which is a type shall be a type-id.

And a typedef is a valid type-id. As a matter of fact the next paragraph contains such an example:

14.3.1 [temp.arg.type] /2

template <class T> class X { };
template <class T> void f(T t) { }
void f() { 
   typedef struct { } B;
   B b;
   X<B> x3;
   f(b);
}

(Where I have trimmed most of the other examples) The example shows that an unnamed type can be used as a class template argument both in class templates and function templates.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Ah! Finally something authoritative. I guess we can infer that if anonymous types in functions work, then anonymous types in struct should word too :) – Matthieu M. May 09 '11 at 18:45
  • your quoting of 14.3.1 [temp.arg.type] /1 is not sufficient. It doesn't say that every *type-id* is a valid template argument. And in fact, `X` uses a type-id too, and is still invalid. – Johannes Schaub - litb May 09 '11 at 20:07
  • @Johannes Schaub: I have reviewed it and you are right that `struct {}` is a `type-id` (I didn't think it was), and yet, the second quote from the standard (the actual example) provides an example in the standard of that being used, which, while not normative, seems to indicate that it is allowed. – David Rodríguez - dribeas May 09 '11 at 22:36
3

A typedef declaration that defines an anonymous class and a typedef-name for that class, the typedef-name is the name of the class for linkage purposes. It is therefore legal to use that class as a template parameter if it meets the other criteria.

See 7.1.3p5 of the C++03 standard

If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the decla-ration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage purposes only (3.5). [Example:

typedef struct { } *ps, S; // S is the class name for linkage purposes

This is 7.1.3p9 in the C++0x FDIS.

FWIW, this code compiles OK with MSVC2010 (modulo typos).

Anthony Williams
  • 66,628
  • 14
  • 133
  • 155
  • Just to be clear: So this supersedes 8.1.3p1 (cited by Mark), and an unnamed class with a typedef-name *does* have linkage? – Konrad Rudolph May 09 '11 at 21:02
  • 2
    It goes with it. The `typedef` itself doesn't introduce a new type, the class definition "inside" the typedef declaration does. The typedef then gives a name for the new type. It is not "the" name for the type --- you can't use it inside the class definition to define the constructor or destructor, for example --- but it provides a name "for linkage purposes". – Anthony Williams May 09 '11 at 21:29
0

Well, that is equivalent to

template <typename T>
struct trait<std::basic_string<T> > {
    struct type {
        T value;
    };
};

which is completely legitimate.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • I was wondering actually, I would consider them equivalent, but is it defined to be equivalent by the standard ? – Matthieu M. May 09 '11 at 16:14
  • 1
    Actually they are similar but not equivalent. The code in the answer defines `trait::type` in the identifier space for user defined types, while the code in the question does not add such an identifier and will add only an alias *outside* of that identifier space. The typedef will still refer to a type that has no name (in the user defined identifier space), and that might be quite relevant to the question. – David Rodríguez - dribeas May 09 '11 at 16:51