2

Is it valid to use an identifier of a class, that is only declared but not defined, as template argument and for template specialization.

Something like that:

template<typename T>
class NodeInfo;

template<typename T>
class GraphInfo;

template<typename T>
class Graph {
public:
    GraphInfo<T> graphInfo;
    NodeInfo<T> nodeInfo;
};


// specialisation
class ContextInfo;

template <>
class NodeInfo<ContextInfo> {
public:
    int a, b, c;

};

template <>
class GraphInfo<ContextInfo> {
public:
    int a, b, c;
};



int main() {
   Graph<ContextInfo> g;
}

This compiles fine without any warning in gcc 7, but I'm wondering if this is a valid thing to do or do I create some kind of undefined behavior with that?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
t.niese
  • 39,256
  • 9
  • 74
  • 101
  • 2
    This is perfectly valid. Now, if the template attempts to do something with its parameter, that requires a defined class, then you'll end up with a compilation failure, but only when you instantiate the template. – Sam Varshavchik Sep 05 '17 at 13:05
  • Beyond being perfectly valid, it's also very useful. You can use a simple tag to specify a domain. For instance `Coordinate` and `Coordinate`. The tag makes those types different, and you can't assign one to another by mistake (a cause of nasty bugs in graphics code). – StoryTeller - Unslander Monica Sep 05 '17 at 13:07
  • 1
    Your code does not access any members of any of the classes, and does not invoke any member functions. The compiler therefore does not need visibility of the definition of the classes you have declared. Do something that requires visibility of the class definition (e.g. accessing a member or calling a member function) and the code will not compile. – Peter Sep 05 '17 at 13:07
  • @Peter Yes I was missing that part, that's true. I for sure want to do `g.graphInfo.a = 1` or something like that, but I never want to initiate `ContextInfo` or use `ContextInfo` of anything else then the template specification. But `g.graphInfo.a = 1` will compile also without any warnings or problems. – t.niese Sep 05 '17 at 13:09

2 Answers2

3

This is fine in principle:

[ Note: A template type argument may be an incomplete type (6.9). — end note ]

However, incomplete class types may not be used with the standard library, unless otherwise specified:

[The] effects are undefined in the following cases: [...]
— if an incomplete type (6.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component.

For example:

The template parameter T of declval may be an incomplete type.

Since C++17, more library facilities permit the use of incomplete types; for example vector:

An incomplete type T may be used when instantiating vector if the allocator satisfies the allocator completeness requirements. T shall be complete before any member of the resulting specialization of vector is referenced.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • I'm not exactly sure about the std part. I know that I cannot use `std::vector` that's obvious. But `std::vector> list` should be valid, because `ContextInfo` is only used for specialization here and `Graph` itself is complete, is that correct? – t.niese Sep 05 '17 at 13:17
  • @t.niese yes, that's correct. Actually, since C++17, you can e.g. write `std::vector` as e.g. a class member, as long as you define `ContextInfo` before using any member functions (incl. the destructor - so you would need to give the parent class a user-defined destructor). – ecatmur Sep 05 '17 at 13:23
1

Yes it's pretty fine. An incomplete type is allowed to used as template argument.

A template argument for a type template parameter must be a type-id, which may name an incomplete type

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Note that it is UB to use incomplete types with some/most classes in `std`, see [why-c-containers-dont-allow-incomplete-types](https://stackoverflow.com/questions/18672135/why-c-containers-dont-allow-incomplete-types). – Jarod42 Sep 05 '17 at 13:15