How to Forward Declare a Template Class in Namespace Std

Forward declare instantiated template class

But this forward declaration doesn't seem to work with instantiated template class, like the commented using Xyz = std::map<int, int>. Can anybody explain why it is so?

Because typedeffing a template does not instantiate the template.
Note that when we instantiate a class template for the first time a new type like C<int> is introduced. But since a typedef-name does not introduce a new type(according to the below quoted statement), the statement using P = C<int>; does not instantiate the class template here.

Same goes for using Xyz = std::map<int, int>.

For example,

template<typename T> 
struct C
{
};
using P = C<int>; //this does no instantiation
//Also note P is an alias for the specialization C<int> and not for the class template named `C`

In the above example, using P = C<int>; does not instantiate the class template.

This can be seen from dcl.typedef#1 which says:

A name declared with the typedef specifier becomes a typedef-name.
A typedef-name names the type associated with the identifier ([dcl.decl]) or simple-template-id ([temp.pre]); a typedef-name is thus a synonym for another type.
A typedef-name does not introduce a new type the way a class declaration ([class.name]) or enum declaration ([dcl.enum]) does.

(emphasis mine)

Additionally, note that P in the above example is an alias for the specialization C<int> and not for the class template named C. A class template is not a class-type by itself. Only the specialization C<int>(etc) is the class type.

How to forward declare a template class in namespace std?

The problem is not that you can't forward-declare a template class. Yes, you do need to know all of the template parameters and their defaults to be able to forward-declare it correctly:

namespace std {
template<class T, class Allocator = std::allocator<T>>
class list;
}

But to make even such a forward declaration in namespace std is explicitly prohibited by the standard: the only thing you're allowed to put in std is a template specialisation, commonly std::less on a user-defined type. Someone else can cite the relevant text if necessary.

Just #include <list> and don't worry about it.

Oh, incidentally, any name containing double-underscores is reserved for use by the implementation, so you should use something like TEST_H instead of __TEST__. It's not going to generate a warning or an error, but if your program has a clash with an implementation-defined identifier, then it's not guaranteed to compile or run correctly: it's ill-formed. Also prohibited are names beginning with an underscore followed by a capital letter, among others. In general, don't start things with underscores unless you know what magic you're dealing with.

Forward declaration of template class in nested namespace: where should default template arguments go?

I have found this as a most convenient workaround:

namespace n1
{
namespace n2
{
template <typename T, typename S>
struct A;
}

namespace fwd { using n2::A; }
}

// stuff on n1::fwd::A;

struct X { };

namespace n1
{
namespace n2
{
template <typename T, typename S = X>
struct A { };
}

using n2::A;
}

That is, move the first using-declaration to another namespace. Now the 'stuff' could go like this:

namespace n1
{
namespace stuff
{
using namespace fwd;

template <typename T>
struct R /*...*/;

template <typename T, typename S>
struct R <A <T, S> > /*...*/;
}
}

using A without any namespaces. There is only one n1 in my project, but lots of n2, so moving all forward using-declarations in a single namespace n1::fwd is more convenient, given that I cannot just use n1.

Forward declaration of template class nested inside template class

Try the following

template<typename T>
template <int N>
class Outer<T>::Inner {};

According to the C++ Standard (14.5.2 Member templates)

1 A template can be declared within a class or class template; such a
template is called a member template. A member template can be defined
within or outside its class definition or class template definition. A
member template of a class template that is defined outside of its
class template definition shall be specified with the
template-parameters of the class template followed by the
template-parameters of the member template.

How to forward declare a template function in global or namespace with default argument?

A default argument, like int i = 0, is seen as a definition. Repeating it is therefore an ODR-violation.

Don't know exactly why, except that the standard explicitly says so

Each of the following is termed a definable item:

[...]

(1.6) a default argument for a parameter (for a function in a given > scope)

[...]
No translation unit shall contain more than one definition of any definable item.

http://eel.is/c++draft/basic.def.odr

The solution is then to only have the default argument appear once, likely in the declaration (and not repeated in the definition).

How do I forward declare a template type that has been forward declared elsewhere with a defaulting

The short answer is that you can't. You either have to guarantee somehow that the other declaration will never be included so you can specify your own default, or you need to depend on that other declaration.

But I get the feeling this is an XY problem, for the following statement:

But I don't want to include Boost in my headers.

Why not? Your code is obviously dependent on Boost's ptree, so anyone working with your code would have to have Boost installed anyway. If you want your declarations to also compile when Boost isn't installed, there are several options:

  • Make sure you exclude the bits that use ptree using preprocessor directives (#if/#endif blocks)
  • Use C++17's __has_include() to test for the existence of <boost/property_tree/ptree.hpp>, and then either include it if it exists, or declare your own ptree if it doesn't.

Can I specialize forward declared template?

The general question about A is that yes, it's allowed. An explicit specialization is disjoint from the primary template. It can be defined as complete or incomplete, regardless of how the primary is defined.

As for your more specific question about std::hash, not it's not alright. You violate

[namespace.std]

1 Unless otherwise specified, the behavior of a C++ program is
undefined if it adds declarations or definitions to namespace std or
to a namespace within namespace std.

2 Unless explicitly prohibited, a program may add a template
specialization for any standard library class template to namespace
std provided that (a) the added declaration depends on at least one
program-defined type and (b) the specialization meets the standard
library requirements for the original template.

Forward declaring std::hash is not a declaration of a specialization that depends on a user defined type. It's a plain declaration that falls under the specification of paragraph 1. There is no wording that allows to forward declare std::hash anywhere else in the standard. So this is undefined behavior.



Related Topics



Leave a reply



Submit