【问题标题】:C++ templated function and forward declarationsC++ 模板函数和前向声明
【发布时间】:2010-09-02 22:01:23
【问题描述】:

我正在编写一些使用 MSVC 在 Windows 上编译和链接(甚至发布了商业产品)的代码。虽然它不能用 GCC 编译,但我收到以下错误:

.../CBaseValue.h: In member function 'bool CBaseValue::InstanceOf()':
.../CBaseValue.h:90:18: error: invalid use of incomplete type 'struct CValueType'
.../CBaseValue.h:11:7: error: forward declaration of 'struct CValueType'

CBaseValue.h

class CValueType;

class CBaseValue {
public:

...

    template <typename _Type>
    bool InstanceOf() {
        CValueType* pType = GetType();
        if(pType == NULL) {
            return false;
        }
        else {
            return pType->IsDerivedFrom<_Type>();
        }
    }

...

}

CValueType.h

class CValueType : public CBaseValue  {
public:

...

    template <typename _Type>
    bool IsDerivedFrom() {
        return IsDerivedFrom(_Type::TYPEDATA);
    }

...

}

我明白为什么这是个问题。基类 (CBaseValue) 有一个使用派生类(在本例中为 CValueType)的模板化函数。

看起来 MSVC 并没有完全遵守 C++ 规范,我只是被它咬了。但是,在调用模板函数的代码实际编译之前使用前向声明的 MSVC 行为现在也更可取。有谁知道一种解决方法,我可以让这个代码与 GCC 一起工作,而不必重写很多基本代码?

从我自己的研究看来,将“-fno-implicit-templates”传递给 g++ 会有所帮助,但我需要明确定义调用的模板类型。它们有很多,所以如果我能避免的话,我会更喜欢它。如果普遍认为这是我最好的选择……那就这样吧!

如果有人想知道,我将代码移植到 Mac 上,这就是我们现在使用 GCC 的原因。

【问题讨论】:

  • 我怀疑模板是一个红鲱鱼。你能拿出模板并确认这仍然是一个问题吗?
  • CBaseValue::InstanceOf 的定义不能在class CValueType 的定义之后移动到CValueType.h 中吗?
  • 不幸的是,我无法取出模板 - 它是整个类型系统(在我们的引擎中)所基于的模板化函数。出于这个原因,我也不能(不更改大量代码)将函数移出 CBaseValue 并移入 CValueType。
  • @tony:我的意思不是在你的生产代码中。我的意思是创建一个最小的可编译测试用例,然后尝试它。如果结果相同,那么您可以简化您的问题标题和代码 sn-p。
  • @tonythetiger:请参阅我的答案,了解我相信 Charles 所指的代码。它不是CValueType的成员,它仍然是CBaseValue的成员,只是使用了外联定义(使用inline关键字保持内联优化语义)。

标签: c++ visual-c++ templates g++ forward-declaration


【解决方案1】:

标准格式不正确,但不需要诊断。 MSVC 可以不诊断 this 特殊情况(即使发生实例化!)。

更具体地说,14.6/7 的 (C++03) 标准规则

如果在非依赖名称中使用的类型在定义模板时是不完整的,但在实例化完成时是完整的,并且如果该类型的完整性影响程序是否格式正确或影响程序的语义,程序格式错误;不需要诊断。

所以解决方案是只使类型依赖,但安排它在实例化期间指定该类型。例如,您可以通过像这样重写模板来做到这一点

template<typename T, typename> // just ignore second param!
struct make_dependent { typedef T type; };

template <typename Type> // eww, don't use "_Type" in user code
bool InstanceOf() {
    typename make_dependent<CValueType, Type>::type* pType = GetType();
    // ...
        return pType->template IsDerivedFrom<Type>();
    // ...
}

【讨论】:

  • “_Type”标识符从来都不是来自代码本身,它只是我放在这里的东西,试图清楚地表明这是我们的一种类型。我这样说是因为我完全同意你的看法!
【解决方案2】:

看来CBaseValue::InstanceOf()函数对不包括CValueType.h的任何人都没用。

所以等待提供定义,直到所有需要的类型都可用。 (编辑:这正是查尔斯·贝利(Charles Bailey)在我打字时发表的评论所暗示的——我想我们的想法是一样的。)

CBaseValue.h

class CValueType;

class CBaseValue {
public:

...

    template <typename _Type>
    bool InstanceOf();

...

}

CValueType.h

class CValueType : public CBaseValue  {
public:

...

    template <typename T>
    bool IsDerivedFrom() {
        return IsDerivedFrom(T::TYPEDATA);
    }

...

}


template <typename T>
inline bool CBaseValue::InstanceOf() {
        CValueType* pType = GetType();
        if(pType == NULL) {
            return false;
        }
        else {
            return pType->IsDerivedFrom<T>();
        }
    }

尽管它们看起来非常紧密耦合,所以也许只有一个用于两个类的头文件,或者一个包含按正确顺序的各个头文件的公共头文件会更好。

【讨论】:

  • 你完全正确,这些类型是非常紧密耦合的——这是一个好主意。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-24
  • 1970-01-01
相关资源
最近更新 更多