【问题标题】:Where to define C++ class member template function and functors that instantiate it?在哪里定义 C++ 类成员模板函数和实例化它的仿函数?
【发布时间】:2011-05-17 23:44:10
【问题描述】:

我有一个 Foo 类,用于一个小型独立项目。它在 Foo.h 中有一个类定义,并在实现文件 Foo.cpp 中实现了该类的成员函数。

第一个问题——Foo类的成员函数之一是模板方法Foo::doSomething(),这个方法的实现应该出现在Foo.h中的函数声明中是否正确?

Foo::doSomething() 将被实例化的模板参数是两种 Functor 类型之一 - 类 CalcA 和 CalcB。

我应该:

  • (A) 将两个 Functor 类的定义和实现全部放在 Foo.cpp 中(它们实际上被其他 Foo 成员函数的实现用来调用 Foo::doSomething)。
  • (B) 把两个Functor类的定义和实现放到Foo.h中。
  • (C) 我是否应该像使用普通类一样在 Foo.h 和 Foo.cpp 中拆分两个 Functor 的定义和实现?

【问题讨论】:

    标签: c++ templates functor


    【解决方案1】:

    首先你必须了解模板机制。模板不被编译,它们在使用时被实例化,然后它们的实例被编译。因此编译器需要使用模板函数在每个模块中都有完整的模板定义,以便根据您传递的参数首先实例化它们。

    要解决您的问题,有三种解决方案,但您会发现它们都导致相同的结果。 您可以在类定义内的头文件中实现整个模板(我们使用 .hxx 而不是 .h 为它们添加后缀,以准确说明它们包含模板定义):

    // Foo.hxx
    
    #ifndef __FOO_HXX__
    #define __FOO_HXX__
    
    class Foo {
      public:
       template <class T>    
       void bar(const T& t) {
          t.doSomething();
       }
     };
    #endif
    

    或者你可以从类中外部化定义,但仍然在头文件中:

    // Foo.hxx
    
    #ifndef __FOO_HXX__
    #define __FOO_HXX__
    
    class Foo {
        public:
           template <class T>    
           void bar(const T&);
    };
    
    template <class T>
    void Foo::bar(const T& t) {
       t.doSomething();
    }
    #endif
    

    最后,您可以在外部文件中实现模板方法主体(出于相同的原因,以 .cxx 为前缀)。它将包含方法的主体,但不包括“Foo.hxx”。相反,它是“Foo.hxx”,它将在类定义之后包含“Foo.cxx”。这样,当编译器解析 #include 指令时,它会在同一个模块中找到整个模板定义,并允许它实例化它:

    // Foo.hxx
    
    #ifndef __FOO_HXX__
    #define __FOO_HXX__
    class Foo {
        public:
           template <class T>    
           void bar(const T&);
    };
    
    #include "Foo.cxx"
    
    #endif
    
    // Foo.cxx
    template <class T>
    void Foo::bar(const T& t) {
       t.doSomething();
    }
    

    这 3 种实现模板的方法之间的选择是一个可读性(和品味)的问题。
    第二和第三在生成的代码方面是等效的,但我宁愿不使用 cxx 文件解决方案,因为当您忘记反转包含时,它通常会导致愚蠢的错误。

    此外,STL 或 Boost 等知名 C++ 库仅在头文件中提出其代码,这是良好设计的标志。通过在标题中使用外部定义,您可以阐明类的定义。您还可以防止编译器自动内联方法,根据 Herb Sutter http://www.gotw.ca/gotw/033.htm

    的说法,这有时会导致结果不佳

    【讨论】:

    • 感谢您的回答。我应该在我原来的问题中提到,在我的情况下,模板成员函数是类 Foo 的私有成员函数。除此之外 - 鉴于您回答中的三个建议,每个建议的优点/缺点是什么?我可以看到的主要区别是,在您的第一个建议中,成员函数将是隐式内联的,而在接下来的两个中则不会,对吗?第二个和第三个有什么优势/劣势?
    • @Czarak:你是对的 -> 类主体中定义的方法会自动内联(有关内联的更多详细信息,请参阅stackoverflow.com/questions/1443982/…)。我编辑了我的答案以添加您要求的精度。希望对您有所帮助。
    【解决方案2】:

    一般规则:

    如果 foo::doSomething() 在 foo.cpp 之外使用(即通常是公共的或受保护的),它必须放在标题中。

    如果没有,放入 cpp 文件是完全可以的,甚至是一个好主意(因为它可以避免头文件的混乱)。

    所以,如果函子只在 cpp 文件中使用,那么一定要把模板函数也放在那里。如果这种情况发生变化,人们总是可以在以后重构。

    【讨论】:

    • 如果只在一个 CPP 文件中需要模板,那么我会将声明和定义放在该文件中。如果它是一个成员函数模板并且该类的其余部分用于其他翻译单元,我肯定会将定义放在 .h 文件中。如果不这样做,我什至会担心未​​定义或实现定义的行为。
    • @Marcus,我应该在最初的问题中提到 - 是的 Foo::doSomething() 是类 Foo 的私有模板成员函数。
    • @John,我不理解你的观点,你能详细说明一下吗?在我的例子中,我有一个类 Foo 的私有模板成员函数,因此它只被 Foo.cpp 中类 Foo 的实现使用。所以这似乎符合您评论中的第一个案例/句子。但我的情况也符合您评论第二句中的描述(“成员函数模板和该类的其余部分用于其他翻译单元”),您说将其放入 .h 文件中。
    • @Czarak:我的意见是默认情况下,您应该将成员函数模板的定义放在 .h 文件中。 @Marcus 可能不同意我的观点。你必须做出判断。
    • 尽管是 private,但在我看来,让未完全定义的对象漂浮在不同的翻译单元周围似乎有点冒险和臭。
    【解决方案3】:

    我的默认设置是将成员函数模板的定义直接放在 .h 文件中,如下所示:

    class Foo
    {
    public: 
      template<typename T> void DoSomething(T t);
    };
    
    // ... later...
    
    template<typename T>
    void Foo::DoSomething(T t)
    {
      // ...
    }
    

    如果这对于特定情况来说不是最理想的,那么我会采取更多英勇的措施。从#include-ing 开始,在 .h 文件末尾定义一个 .inc 文件,或者甚至可能在需要使用成员函数模板的 .cpp 文件中进行显式实例化。

    【讨论】:

    • 恕我直言,如果它只是一个功能,那就有点过度设计了。但无论如何,这是一个自行车棚的话题。 :)
    • @Marcus:过度设计具体是什么?
    • @Marcus:我想我明白你现在在说什么了
    【解决方案4】:

    模板方法定义确实应该在它所属的类的头文件中。

    像这样:

    class MyClass
    {
        template <typename T>
        void foo(const T&)
        {
            // Definition
        }
    };
    

    或者像这样(注意模板方法定义可以在类声明之后从单独的文件中包含)

    class MyClass
    {
        template <typename T> void foo(const T&);
    };
    
    template <typename T>
    void MyClass::foo(const T&)
    {
        // Definition
    }
    

    其余的取决于您同意的样式和您的需求。

    如果我不仅在 Foo 中使用它们,或者如果 Foo 将它们作为类成员,我会将仿函数声明(如果它们很简单,甚至是定义)放入标题中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-10
      • 1970-01-01
      相关资源
      最近更新 更多