简体   繁体   English

前向声明实例化模板 class

[英]Forward declare instantiated template class

We forward declare class in the api.h files, like the struct Abc in example below, because we only use std::shared_ptr<Abc> This has the advantage we can change the definition of struct Abc without recompiling api.h related files. We forward declare class in the api.h files, like the struct Abc in example below, because we only use std::shared_ptr<Abc> This has the advantage we can change the definition of struct Abc without recompiling api.h related files. But this forward declaration doesn't seem to work with instantiated template class, like the commented using Xyz = std::map<int, int>但是这个前向声明似乎不适用于实例化模板 class,就像using Xyz = std::map<int, int>评论的那样

Can anybody explain why it is so?谁能解释为什么会这样? and how we can forward delare something like std::map<int, int> ?以及我们如何转发 delar 类似std::map<int, int>的东西? We have a "work-around": instead of using Xyz = std::map<int, int> , we can do class Xyz: std::map<int, int> , this is the only line we need to change.我们有一个“解决方法”:我们可以使用class Xyz: std::map<int, int>而不是using Xyz = std::map<int, int> , int> ,这是我们需要更改的唯一行。 But it is strongly recommended not derive from STL.但强烈建议不要从 STL 派生。 So any other suggestions?那么还有其他建议吗?

// lib.h
#pragma once
#include <memory>
#include <map>
namespace ABC {
    struct Abc {
        int abc;
    };
    using AbcPtr = std::shared_ptr<const Abc>;
    int impl(const AbcPtr& o);

    // using Xyz = std::map<int, int>;
    // using XyzPtr = std::shared_ptr<const Xyz>;
    // int impl2(const XyzPtr& o);
}

// ------------
// lib.cpp
#include "lib.h"
int ABC::impl(const ABC::AbcPtr& o) {
    return o->abc;
}
// int ABC::impl2(const ABC::XyzPtr& o) {
    // return static_cast<int>(o->size());
// }

// ------------
// api.h
#pragma once
#include <memory>
namespace ABC {
    struct Abc;
    using AbcPtr = std::shared_ptr<const Abc>;
    // class Xyz;
    // using XyzPtr = std::shared_ptr<const Xyz>;
}

namespace Api {
    int api(const ABC::AbcPtr& o);
    // int api2(const ABC::XyzPtr& o);
}
int main();

// ------------
// api.cpp
#include "api.h"
#include "lib.h"
int Api::api(const ABC::AbcPtr& o) {
    return ABC::impl(o);
}
// int Api::api2(const ABC::XyzPtr& o) {
    // return ABC::impl2(o);
// }
int main() {
    auto obj = std::make_shared<ABC::Abc>();
    obj->abc = 100;
    int res1 = Api::api(obj);
    
    // auto obj2 = std::make_shared<ABC::Xyz>();
    // obj2->insert({ 3, 4 });
    // int res2 = Api::api2(obj2);
    return res1;
}

But this forward declaration doesn't seem to work with instantiated template class, like the commented using Xyz = std::map<int, int> .但是这个前向声明似乎不适用于实例化模板 class,就像using Xyz = std::map<int, int>评论的那样。 Can anybody explain why it is so?谁能解释为什么会这样?

Because typedeffing a template does not instantiate the template.因为 typedefing 模板不会实例化模板。 Note that when we instantiate a class template for the first time a new type like C<int> is introduced.请注意,当我们第一次实例化 class 模板时,会引入像C<int>这样的新类型。 But since a typedef-name does not introduce a new type(according to the below quoted statement), the statement using P = C<int>;但是由于typedef-name没有引入新类型(根据下面引用的语句), using P = C<int>;的语句does not instantiate the class template here.此处不实例化 class 模板。

Same goes for using Xyz = std::map<int, int> . 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>;在上面的例子中, using P = C<int>; does not instantiate the class template.不实例化 class 模板。

This can be seen from dcl.typedef#1 which says:这可以从dcl.typedef#1中看出:

A name declared with the typedef specifier becomes a typedef-name.使用 typedef 说明符声明的名称成为 typedef-name。 A typedef-name names the type associated with the identifier ([dcl.decl]) or simple-template-id ([temp.pre]); typedef-name 命名与标识符 ([dcl.decl]) 或 simple-template-id ([temp.pre]) 关联的类型; a typedef-name is thus a synonym for another type.因此 typedef-name 是另一种类型的同义词。 A typedef-name does not introduce a new type the way a class declaration ([class.name]) or enum declaration ([dcl.enum]) does. typedef-name 不会像 class 声明 ([class.name]) 或枚举声明 ([dcl.enum]) 那样引入新类型。

(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 .此外,请注意,上述示例中的P是特化C<int>的别名,而不是名为C的 class 模板的别名。 A class template is not a class-type by itself . class 模板本身不是类类型 Only the specialization C<int> (etc) is the class type.只有专业化C<int> (等)是 class 类型。

What you are trying to do is not directly possible for standard library types.您尝试做的事情对于标准库类型来说是不可能的。

class Xyz; declares a class with the name Xyz but using Xyz = is just an alias which doesn't introduce any new type.声明了一个名为 Xyz 的Xyzusing Xyz =只是一个别名,它不引入任何新类型。 If you declare both of them in the same scope they just conflict.如果您在同一个 scope 中声明它们,它们只会发生冲突。 The alias refers to a specialization of std::map , not to a class ABC::Xyz and an alias does not identify one with the other.别名是指std::map的特化,而不是 class ABC::Xyz Xyz 并且别名不能识别一个与另一个。

If you want to do the analogue of what you were doing with struct Abc;如果您想做与struct Abc; , you would need to forward-declare std::map instead of class Xyz; ,您需要前向声明std::map而不是class Xyz; , eg ,例如

template<typename, typename, typename, typename> class map;

But the problem is that you would need to do that in the std namespace and you are not allowed to add declarations to the std namespace (with some exceptions that don't apply here).但问题是您需要在std命名空间中执行此操作,并且不允许将声明添加到std命名空间(有一些在这里不适用的例外情况)。 It causes undefined behavior.它会导致未定义的行为。

So I think you don't really have a choice but to define your own class wrapping std::map in some way, eg as you suggested by inheriting from it.所以我认为你别无选择,只能以某种方式定义你自己的 class 包装std::map ,例如,正如你通过继承它所建议的那样。 If you use your own types, you can just add the forward-declaration as mentioned above in the appropriate namespace.如果您使用自己的类型,则只需在适当的命名空间中添加上述前向声明即可。

(I do however doubt that you would need this for standard library types anyway. You have no control over their implementation. The only real advantage would be faster compilation time due to not having to include <map> in as many translation units.) (但是我怀疑你是否需要这个标准库类型。你无法控制它们的实现。唯一真正的优势是更快的编译时间,因为不必在尽可能多的翻译单元中包含<map> 。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM