Oracle® Solaris Studio 12.4: C++ User's Guide

Exit Print View

Updated: March 2015
 
 

1.1.2 Enforcement of C++ Rules

The Oracle Solaris Studio 12.4 C++ 5.13 compiler enforces some C++ rules more strictly than past compilers. The incorrect rule enforcement caused earlier compilers to treat some valid code incorrectly. This section explains the stricter enforcement, provides examples of offending code, and shows how to fix the code. In all cases, you can change your code and it should still work with old and new compilers.

1.1.2.1 Template definition parsing

Previous compilers did not parse template definitions until the template was instantiated, with the following consequences:

  1. Previous compilers would allow use of names not declared until after the template definition. Invalid code could be accepted, valid code could be rejected due to an apparent conflict, or the wrong declaration could be used.

  2. Unconditional errors in templates that were not instantiated would not be diagnosed.

  3. Names in a the definition of template T that do not depend on template parameters would not be instantiated until T was instantiated. They should be instantiated implicitly at the point where T is defined.

Example 1:

template< class T > int f(int i = j) // j is not visible
  { return i; }
int j = 2;
int main()
{
    return f<int>();
}

Older compilers accepted this code because template f was not parsed until after j was defined.

Solution: Move the declaration of j ahead of the template.

Example 2:

#include <stdio.h>
void f(double d) { printf("f(double)\n"); }
template< class C > struct B {
    B() { f(1); } // f is not dependent on template parameter
};
void f(int d) { printf("f(int)\n"); }
int main()
{
    B<int> b; // should print "f(double)"
}

Solution: Be sure any declarations that the template should depend on occur before the template definition.

1.1.2.2 Dependent base lookup

When looking up an unqualified name in a template definition, base classes that depend on a template parameter should not be examined. Previous compilers did incorrect name lookup.

Example:

template <typename T> struct Base { };

template <typename T> struct Derived : Base <T>
{
    Derived() : Base() { } // unqualified Base should not be found
};
Derived <int> x;

template <typename T> struct Derived2 : Base <T>
{
    Derived2() : Base<T>() { } // OK
};
Derived2<int> x2;

int main() { }

In class Derived, the use of unqualified Base without template arguments is not valid.

Solution: In class Derived2, the usage is correct.

1.1.2.3 Re-declaring template parameters

You can no longer re-declare the name of a template parameter.

Example:

template <typename T> class A { typedef int T; }; // re-declare T

Solution: Pick a different name for the template parameter or for the local name.

1.1.2.4 Old-style explicit instantiations

The pre-1998 style of declaring an explicit specialization without template<> is no longer allowed.

Example:

template <typename T> class A { static T m; };
int A<int>::m = 0; // now an error

Solution:

template<> int A<int>::m = 0;

1.1.2.5 Stricter -template=extdef

Refer to Template Definitions Separate. The "template definitions separate" compilation model is now more strict. When creating the X.cc file that has the definitions of templates declared in header X.h, you must be especially careful not to have anything else (that is, other than items directly associated with the definitions) in that file. You are now more likely to get "multiple definition" errors if you violate the rule.

Example:

% cat extdef.h
template <typename T> T foo(T);
% cat extdef.cc
#include "extdef.h"
int main() {
        foo(1);
}
% CC extdef.cc -template=extdef
"extdef.cc", line 3: Error: main() already had a body defined.
1 Error(s) detected.

Solution: Remove everything from the X.cc file that is not needed for the template definitions. Recall that the X.cc file is not supposed to be explicitly compiled, because it is automatically included whenever the X.h file is included.

If extensive changes are needed, you might consider compiling with the -template=no%extdef option. That behavior is the default for other compilers, and is now the default in this release.

1.1.2.6 Implicit int

An implicit declaration of type int was never allowed in C++, but earlier compilers sometimes allowed it with a warning. Due to the way it interferes with proper template handling, the compiler no longer assumes you meant to declare something type int.

Example:

static i = 0; // now an error

Solution: Provide the type explicitly in the declaration.

1.1.2.7 Friend declarations

When a friend declaration in class C for a function or class T was the first declaration of T, previous compilers incorrectly inserted the declaration of T into the surrounding scope. The Oracle Solaris Studio 12.4 C++ 5.13 compiler no longer does so, because it can lead to incorrect interpretation of valid programs.

Example:

class A
{
    friend class B;    // not previously declared
    friend void foo(); // not previously declared

    B* bar() // Error: B is not defined.
    {
        foo(); // Error: The function "foo" must have a prototype.
        return 0;
    }
};

Solution: Declare the friend function or class in a scope outside the class before the class declaration that declares it a friend

class B;
void foo();
class A
{
    friend class B;    // refers to prior declaration
    friend void foo(); // refers to prior declaration

    B* bar() // OK
    {
        foo(); // OK
        return 0;
    }
};

1.1.2.8 Static functions and name lookup in templates

  1. When looking up names in a dependent function call, previous compilers incorrectly ignored static functions that were in scope. The Oracle Solaris Studio 12.4 C++ 5.13 compiler now treats static and extern functions the same way.

    Example:

    // previous compiliers ignored this bar() in dependent name lookup
    static int bar(int)
    {
            return 1;
    }
    int bar(long)
    {
            return 0;
    }
    template <typename T>
    int foo(T t)
    {
    	// function call depends on template argument
            return bar(t);
    }
    int main()
    {
            return foo(0);
    }

    Compiled with previous compilers, the program would return 0. With the Oracle Solaris Studio 12.4 C++ 5.13 compiler, the program returns 1.

  2. When only a static function was found in name lookup, previous compilers would issue an error like

    "Reference to static bar(int) not allowed in template foo(int), 
    try using -features=tmplrefstatic."

    See the previous example, with extern function bar(long) removed. The compiler now issues only a warning in -compat=5 mode because the code is technically an extension to the C++03 standard. In C++11 mode, the code is silently accepted because it is valid C++11.

  3. The option -features=no%tmplrefstatic is still accepted, but has no net effect because references to static objects from templates are always allowed.