Oracle® Solaris Studio 12.4:C++ 用户指南

退出打印视图

更新时间: 2014 年 12 月
 
 

1.1.2 C++ 规则的实施

Oracle Solaris Studio 12.4 C++ 5.13 编译器在实施某些 C++ 规则时比过去的编译器严格得多。规则实施错误导致早期的编译器无法正确处理某些有效代码。本节说明更严格的实施,提供错误代码的示例,并显示如何修复代码。在所有这些例子中,您都可以更改代码,随后该代码应在新旧编译器中正常运行。

1.1.2.1 模板定义解析

以前的编译器不解析模板定义,直到实例化模板后才会进行解析,这会产生以下后果:

  1. 以前的编译器直到定义模板后才允许使用未声明的名称。这可能会因明显的冲突而导致无效代码被接受,有效代码被拒绝,或使用错误的声明。

  2. 无法诊断出未实例化的模板中的无条件错误。

  3. 只有在实例化模板 T 后,才会实例化模板 T 的定义中不依赖于模板参数的名称。这些名称应在定义 T 时隐式实例化。

示例 1:

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

早期的编译器接受此代码,因为只有在定义了 j 后才会解析模板 f

解决方法:在定义模板之前声明 j

示例 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)"
}

解决方法:确保在定义模板之前进行模板所依赖的任何声明。

1.1.2.2 相关基本查找

在模板定义中查找未限定的名称时,不应检查依赖于模板参数的基类。以前的编译器对名称的查找不正确。

示例:

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() { }

在类 Derived 中,使用未限定的 Base 而没有模板参数是无效的。

解决方法:在类 Derived2 中,这种用法是正确的。

1.1.2.3 重新声明模板参数

您不能再重新声明模板参数的名称。

示例:

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

解决方法:为模板参数或本地名称选择其他名称。

1.1.2.4 旧样式显式实例化

不再允许使用 1998 之前的不带 template<> 的显式专门化声明样式。

示例:

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

解决方法:

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

1.1.2.5 更严格的 -template=extdef

请参阅独立的模板定义。“独立模板定义”编译模型现在更严格了。如果要创建的 X.cc 文件包含在头文件 X.h 中声明的模板的定义,必须特别小心,不要在该文件中添加其他任何内容(即,除了与定义直接相关的项目之外的任何内容)。现在如果违反了此规则,则可能得到“多个定义”错误。

示例:

% 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.

解决方法:从 X.cc 文件中删除模板定义不需要的任何内容。请记得 X.cc 文件无需显式编译,因为只要包括 X.h 文件,就会自动包括它。

如果需要进行其他更改,可考虑使用 -template=no%extdef 选项进行编译。此行为是其他编译器的缺省行为,现在也是此发行版的缺省行为。

1.1.2.6 隐式 int

在 C++ 中从不允许隐式声明类型 int,但早期的编译器有时允许声明并显示警告。由于这干扰了正常的模板处理过程,因此编译器不再假定您要声明类型 int

示例:

static i = 0; // now an error

解决方法:在声明中显式提供类型。

1.1.2.7 友元声明

如果类 C 中函数的友元声明或类 T 是 T 的第一个声明,以前的编译器会错误地将 T 的声明插入周围作用域。Oracle Solaris Studio 12.4 C++ 5.13 编译器不再进行此操作,因为这会导致对有效程序进行错误解释。

示例:

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;
    }
};

解决方法:在将类声明为友元的类声明之前,在该类之外的作用域中声明友元函数或类。

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 模板中的静态函数和名称查找

  1. 在相关函数调用中查找名称时,以前的编译器会错误地忽略作用域中的静态函数。Oracle Solaris Studio 12.4 C++ 5.13 编译器现在以同样的方式处理 staticextern 函数。

    示例:

    // 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);
    }

    使用以前的编译器编译时,程序会返回 0。使用 Oracle Solaris Studio 12.4 C++ 5.13 编译器时,程序将返回 1。

  2. 如果在名称查找中只找到静态函数,以前的编译器会发出类似如下的错误

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

    请参见前面的示例,删除 extern 函数 bar(long)。编译器现在只在 -compat=5 模式下发出警告,因为该代码在技术上是对 C++03 标准的扩展。在 C++11 模式中,将接受此代码而不显示任何提示,因为它是有效的 C++11。

  3. 仍接受选项 -features=no%tmplrefstatic,但它没有实际作用,因为始终允许对模板中的静态对象的引用。