Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Types With No Linkage as Template Arguments

Last updated Jan 1, 2003.

The C++03 standard says that local types, unnamed types and in general, types with no linkage, shall not be used as template arguments. This restriction was removed from the C++09. Find out here what the rational behind the rules relaxation is and how implementations should cope with these changes.

C++03 Restrictions on Template Parameters

The restrictions on types that are used as template parameters are listed in article 14.3.1 of the C++03 (and C++98) standard:

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.

template <class T> class Y { /* ... */ };
void func()
{
  struct S { /* ... */ }; //local class
  Y<S> y1; // error: local type used as template-argument
  Y<S*> y2; // error: pointer to local type used as template-argument
}

Consistency

From a C++ programmer's perspective, there's not much difference between a local class (i.e., a class declared inside a function), and classes declared in a namespace or a class scope. It's therefore surprising that classes defined inside a function are treated as a special case. Similarly, an unnamed enum declared at namespace scope is excluded from template type deduction, whereas a named enum is permitted:

template<typename T> void f(const T & t){}
struct S{};
int main()
{
 struct U{};
 foo(S()); // well-formed
 foo(U()); // ill-formed
}
enum E { x };
enum {y}; //unnamed enum
 
int main()
{
 foo(x); // well-formed
 foo(y); // ill-formed
}

These C++03 restrictions lead to an artificial distinction between categories of types that merely confuses programmers and lacks sufficient motivation.

Usage

The examples shown above might have seemed contrived in the late 1990s. However, today C++ code relies by and large on templates and algorithms. Traditional arrays and pointers are replaced with vector and shared_ptr, respectively. Likewise, for-loops are often replaced with a for_each algorithm call. There are many cases in which a type is only useful within the context of a particular function. In these cases, it makes sense to declare the class or enumeration as local types that are accessible only from their enclosing function. There's no reason why such local types and instances thereof should not be valid template arguments.

The proposal to relax these restrictions was voted into the C++09 Committee Draft some time ago. Under the said proposal, the problems shown above vanish. Local types and unnamed types now work with template-based algorithms and containers just as do types with external linkage. How do implementations handle the new rules?

Implementation Issues

According to the new C++09 rules, most types that have hitherto been classified as types with no linkage, shall now have external linkage. These include:

  • Unnamed enumerations
  • Enumerators belonging to unnamed enumerations
  • Names declared in a local scope (e.g., local classes)

To reduce compatibility issues and in order to avoid other implementation complexities, local types in C++09 have the same linkage as their enclosing function. Unnamed types have the same linkage they would have if they were named. In other words, the new rules reduce the number of type categories that have no linkage. The only category that remains with no linkage in C++09 is types declared within namespace-scope static functions:

template<typename T>
void func(T t);
static void f() //note: deprecated use of static, see discussion below
{
 enum {Z}; //no linkage in both C++0x and C++09
 func(Z); //error
}
void g()
{
 class Y{}; //has external linkage in C++09 because g() has external linkage
 enum {Z}; //has external linkage in C++09 because unnamed types are treated
 // as a named types. A named enum inside g() has external linkage 
 //because g() has external linkage
 func(Y()); //OK
 func (Z); //OK
}
int main()
{}

Recall that namespace scope static functions are deprecated in standard C++. Instead, you're advised to declare f() in an unnamed namespace. Identifiers declared in an unnamed namespace have external linkage. However, they are virtually invisible to external translation units because the compiler assigns special mangled names to them. By the same token, local types and unnamed types shall have external linkage in C++09 (unless declared inside a namespace-scope static function) but their names shall be invisible from outside the scope in which they are declared.

In Conclusion

Local classes and enums, as well as unnamed enums can now be used like any other type with external linkage. How implementations cope with the new rules (e.g., by promoting the linkage of such types from "no-linkage" to "external linkage") isn't particularly interesting from an end user perspective; however, programmers will now face less obstacles using local classes and unnamed types as template arguments.