“多重定义”当使用(mock)头文件的模板 [英] "Multiple definition" when using (mock) header files for templates

查看:148
本文介绍了“多重定义”当使用(mock)头文件的模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道C ++模板函数的定义必须放在头文件中。然而,为了提高(潜在)大库的可读性和结构的原因,我将声明从实现分离为mock头(其中 #include 实施文件,非常类似于文件的此结构)。 请注意,我知道在编译时必须包含模板函数的实现



简而言之,当我将非模板函数声明添加到实现文件时,我有一个多重定义错误。详细说明如下。



当mock标题+实现文件对只包含模板函数的声明/ ,一切工作正常。当我在实现文件中添加一个新的模板函数的实现时,它也可以正常工作。



工作示例(我会在主中 #includealgo.h cpp ):



Mock头文件:algo.h

  #ifndef ALGO_H 

namespace fl {
template< typename Compare>
void algo(.. non-templated args ..,Compare order = std :: less< int>());
}

#includetpp / algo.cpp

#endif // ALGO_H

实施文件 tpp / algo.cpp .tpp )

注意:使用 tpp / .cpp 文件位于初始版本我使用 .tpp 文件每 @πάνταῥῖ的建议,最后解释。

  #ifndef TPP_ALGO 
#define TPP_ALGO

#include../algo.h

namespace fl {

template< typename Compare>
void subFunctionality(比较顺序,.. args ..){/ * impl * /}

模板< typename比较>
void algo(.. non-templated args ..,Compare order){
subFunctionality(order,.. args ..);
// implementation
}
}

#endif // TPP_ALGO

当我在实现文件中添加非模板函数实现时,会出现问题。 (目前只是 algo.tpp ) (使用)的 tpp / algo.cpp 同一个algo.h ):

  #ifndef TPP_ALGO 
#define TPP_ALGO

#include../algo.h

命名空间fl {

模板< typename比较>
void subFunctionality(比较order,.. args ..){/ * impl * /}

void moreSubFun(.. args ..){/ implion * /}

template< typename比较>
void algo(.. non-templated args ...,比较顺序){
subFunctionality(order,.. args ..);
moreSubFun(.. args ..);
//更多内容
}
}

#endif // TPP_ALGO

我得到多重定义错误(从我在 main.cpp 中包括它)像这样:

  obj / Release / main.o在函数`fl :: moreSubFun(...)':
main.cpp`fl :: moreSubFun(..)'的多个定义
obj / Release /../ tpp / algo.o:algo.cpp首先在这里定义



为什么这种情况只发生在非模板函数上,而对于模板 我如何解决这个问题?



我看了所有的SO,我找不到任何有用的:(理想情况下,对于尽可能接近我自己的文件结构的东西(但我会把任何工作,同时仍然使用一些分离为mock .h + tpp / .cpp )。我必须将额外的子功能带入一个单独的,非模板对 .h / .cpp 文件,还是有其他解决方案? (子功能最好不会对最终用户可见)。



我不愿意使用 inline 定义 fl :: moreSubFunc(..)时,函数很大(我被教 inline 理想地只使用小功能)。这确实解决了问题,但我正在寻找是否有不同的解决方案。



我在工作Code :: Blocks ,使用 gcc version 4.7.2 。这是我的实现文件 tpp / .cpp .cpp 扩展)的最初原因,因为 Code :: Blocks 默认不支持它。这在当前实现中已更改 @πάνταῥῖ的建议(见下文)。






延迟编辑(教我找到解决方案后)我教了 @πάνταῥεῖ的回答解决问题。我调整了 Code :: Blocks 以接受 .tpp 文件(将其视为文件)。



但是,此解决方案仅在 algo.h 文件只包含在一个其他文件:当我将它仅包含在 main.cpp 中时。但是,一旦我尝试将其包含在使用这些算法(除了 main.cpp )的另一个源文件(例如 algo2.cpp )中, >多重定义问题。



底线,问题仍然存在,只要我包含 algo.h <

解决方案

您的问题发生,因为功能模板在链接时从简单自由功能处理不同。函数必须遵守一种定义规则(ODR);也就是说,它们必须在不超过一个翻译单元中定义。否则,当你得到链接时间,你最终得到多个定义错误,如你引用的。同样的规则也适用于类,类型和对象。



这似乎排除了模板的使用。它们必须完全包含在使用它们的每个翻译单元中。但是,ODR在少数情况下会出现异常。从维基百科中引用:


有些东西,例如类型,模板和外部内联函数,可以在多个翻译单元中定义。对于给定实体,每个定义必须相同。


这就是为什么不同翻译单元中的非extern对象和函数是不同的实体您不会遇到与模板函数的多个定义错误。在链接时,链接器找到重复的符号定义,并删除所有重复的(只要它们都是等价的;否则,这将是一个错误)。因此,您的程序将成功地与每个必需符号的一个定义成功链接。



对于您的情况,会出现您的问题,因为您在多个翻译中包含非模板函数单元(随处包括 .cpp 文件)。有几种方法可以修复此问题:


  1. 如果模板函数是类的一部分, -template函数也在那个类中。


  2. 将函数标记为内联。


  3. 将非模板函数分成另一个 .cpp 文件,然后单独编译。这将是唯一的翻译单元,以容纳他们。



I am aware that definitions of C++ templated functions have to be placed in header files. However, for reasons of improved readability and structure of a (potentially) big library I am making, I separated the declarations from the implementations, into "mock" headers (which #include the implementation files, quite like this structure of files). Note that am I am aware that the implementation of templated functions must be included at compile time, and I am doing that.

In short, I have a "multiple definition" error when I add a non-templated function declaration into the implementation file. Long explanation with examples follows.

When the pair of "mock" header + implementation files only contain the declaration/implementation pair of the templated function, everything works fine. It also works fine when I add an implementation of a new templated function only in the implementation file.

Working example (I would #include "algo.h" in my main.cpp when I wanted to use this functionality):

"Mock" header file algo.h:

#ifndef ALGO_H
#define ALGO_H

namespace fl{
    template <typename Compare>
    void algo(.. non-templated args .., Compare order = std::less<int>());
}

#include "tpp/algo.cpp"

#endif // ALGO_H

Implementation file tpp/algo.cpp: (currently just algo.tpp)
Note: Using the tpp/.cpp file was in the initial version, now I am using a .tpp file per @πάντα ῥεῖ's suggestion, explanation in the end.

#ifndef TPP_ALGO
#define TPP_ALGO

#include "../algo.h"

namespace fl{

    template <typename Compare>
    void subFunctionality(Compare order, .. args ..){ /* impl */ }

    template <typename Compare>
    void algo(.. non-templated args .., Compare order){
         subFunctionality(order, .. args ..);
        // implementation
    }
}

#endif // TPP_ALGO

The problem arises when I add a non-templated function implementation in the implementation file. (Non-working) example of the tpp/algo.cpp (currently just algo.tpp) (using the same algo.h):

#ifndef TPP_ALGO
#define TPP_ALGO

#include "../algo.h"

namespace fl{

    template <typename Compare>
    void subFunctionality(Compare order, .. args ..){ /* impl */ }

    void moreSubFun(.. args ..) { /* impl */ }

    template <typename Compare>
    void algo( .. non-templated args ..., Compare order){
         subFunctionality(order, .. args ..);
         moreSubFun(.. args ..);
         // more stuff
    }
}

#endif // TPP_ALGO

I get the "multiple definition" error (from where I included it in main.cpp), like so:

obj/Release/main.o                 In function `fl::moreSubFun(...)':
main.cpp                           multiple definitions of `fl::moreSubFun(..)'
obj/Release/../tpp/algo.o:algo.cpp first defined here

Why does this happen only to non-templated functions, while it works fine for the templated, and more importantly, how do I solve this problem?

I looked all around SO, and I can't find anything useful :( Ideally, I am looking for something as close to my own file-structure as possible (but I'll take anything that works while still using some separation into "mock" .h + tpp/.cpp). Do I have to take out the additional sub-functionalities into a separate, non-templated pair of .h/.cpp files, or is there other solutions? (The sub-functionalities should ideally not be visible to the end-user).

I am reluctant to use inline when defining fl::moreSubFunc(..) as the function is pretty big (and I was taught inline should ideally only be used with small functions). This does solve the problem, but I'm looking to see if there is a different solution.

I am working in Code::Blocks, using gcc version 4.7.2. This is the initial reason my implementation file is tpp/.cpp (.cpp extension), since Code::Blocks does not support it by default. This is changed in the current implementation following @πάντα ῥεῖ's suggestion (look below).


Late edit (After I taught the solution was found) I taught @πάντα ῥεῖ's answer solves the problem. I tweaked Code::Blocks to accept .tpp files (either treating it as header or source files). Initially, this solution worked.

However, this solution worked only when the algo.h file was included in only one other file: when I included it only in main.cpp. However, as soon as I tried including it in another source file (e.g. algo2.cpp) that would use those algorithms (in addition to main.cpp), the multiple definition problem came back.

Bottom line, the problem still persists as soon as I include the algo.h in more than one file, and I am still looking for a solution.

解决方案

Your problem occurs because function templates are treated differently at link time from "plain" free functions. Functions must obey the One Definition Rule (ODR); that is, they must be defined in no more than one translation unit. Otherwise, when you get to link time, you end up with multiple definition errors like the one you cited. This same rule also applies to classes, types, and objects in general.

This would seem to preclude the use of templates at all; they must be fully included in every translation unit in which they are used. However, the ODR makes an exception for a few cases. Quoting from Wikipedia:

Some things, like types, templates, and extern inline functions, can be defined in more than one translation unit. For a given entity, each definition must be the same. Non-extern objects and functions in different translation units are different entities, even if their names and types are the same.

This is why you don't run into multiple definition errors with the template functions. At link time, the linker finds the duplicate symbol definitions and removes all duplicates (as long as they are all equivalent; otherwise, this would be an error). Therefore, your program links successfully with exactly one definition of each required symbol.

For your case, your problem occurs because you are including non-template functions in more than one translation unit (everywhere that the .cpp file is included). There would be a few ways of fixing this:

  1. If the template functions are part of a class, you could move the non-template functions to lie in that class as well. This would bring it under the symbol-deduplicating umbrella of the owning template class.

  2. Mark the functions as inline.

  3. Break the non-template functions out into another .cpp file that you then compile separately. That will be the only translation unit that houses them.

这篇关于“多重定义”当使用(mock)头文件的模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆