【问题标题】:linker complains about multiple definition even there are inclusion guards即使有包含保护,链接器也会抱怨多重定义
【发布时间】:2017-11-28 10:56:14
【问题描述】:

我无法弄清楚为什么代码无法编译,即使我有包含保护以防止重复编译

主类foo.h:

#ifndef FOO_H_INCLUDED
#define FOO_H_INCLUDED

#include <iostream>

class Foo;

template<int TDim>
struct Foo_Helper
{
    static bool Compare(const Foo& this_foo, const Foo& other_foo);
};

class Foo
{
public:
    Foo(const int& Value) : mValue(Value) {}
    virtual ~Foo() {}
    const int& Value() const {return mValue;}
    template<int TDim>
    bool Compare(const Foo& rother_foo) {return Foo_Helper<TDim>::Compare(*this, rother_foo);}
private:
    int mValue;
};

#endif

#include "foo.hpp"

foo.hpp 来定义模板特化:

#ifndef FOO_HPP_INCLUDED
#define FOO_HPP_INCLUDED

template<>
bool Foo_Helper<1>::Compare(const Foo& this_foo, const Foo& other_foo)
{
    return this_foo.Value() == other_foo.Value();
}

template<>
bool Foo_Helper<2>::Compare(const Foo& this_foo, const Foo& other_foo)
{
    return this_foo.Value() == other_foo.Value();
}

#endif

两个源文件: src1.cpp:

#include "foo.h"

class Test1
{
public:
    bool test()
    {
        Foo f1(1);
        Foo f2(2);

        return f1.Compare<1>(f2);
    }
};

src2.cpp:

#include "foo.h"

class Test2
{
public:
    bool test()
    {
        Foo f1(1);
        Foo f2(2);

        return f1.Compare<2>(f2);
    }
};

CMakeLists.txt:

set(file_list
src1.cpp
src2.cpp
)

add_library(Test SHARED ${file_list})

错误信息:

Linking CXX shared library libTest.so
CMakeFiles/Test.dir/src2.cpp.o: In function `Foo_Helper<1>::Compare(Foo const&, Foo const&)':
/home/hbui/workspace/c++/multiple_definition_error/foo.hpp:7: multiple definition of `Foo_Helper<1>::Compare(Foo const&, Foo const&)'
CMakeFiles/Test.dir/src1.cpp.o:/home/hbui/workspace/c++/multiple_definition_error/foo.hpp:7: first defined here
CMakeFiles/Test.dir/src2.cpp.o: In function `Foo_Helper<2>::Compare(Foo const&, Foo const&)':
/home/hbui/workspace/c++/multiple_definition_error/foo.hpp:13: multiple definition of `Foo_Helper<2>::Compare(Foo const&, Foo const&)'
CMakeFiles/Test.dir/src1.cpp.o:/home/hbui/workspace/c++/multiple_definition_error/foo.hpp:13: first defined here
collect2: error: ld returned 1 exit status
make[2]: *** [multiple_definition_error/libTest.so] Error 1
make[1]: *** [multiple_definition_error/CMakeFiles/Test.dir/all] Error 2
make: *** [all] Error 2

我认为包含保护阻止了 foo.hpp 中的两个函数被编译。但是,看起来每个 cpp 文件都编译了自己的函数。在这种情况下,如何正确定义模板专用函数?

【问题讨论】:

  • 您将链接与编译混淆了。标头保护防止编译器(正在查看单个 cpp -> 对象转换)查找多个定义。链接器(需要多个对象 -> lib/exe)尝试将所有函数拼凑在一起。
  • 是的,但是解决办法是什么?
  • 旁白:为什么你的模板有两个具有相同主体的特化?如果有人拨打Foo::Compare&lt;3&gt;会怎样?
  • @Caleth,可以修改 Helper 中的 Compare 函数以提供默认行为,可能会引发错误。
  • @kstn 它可以,但两个版本都不是。为什么不只是一个免费功能bool operator==(const Foo&amp; this_foo, const Foo&amp; other_foo);

标签: c++ templates


【解决方案1】:

标头保护阻止标头在同一个翻译单元中包含两次。您的问题是 Compare 专业化的定义将在两个翻译单元中定义,但未标记为 inline。这意味着当链接器试图链接由 test1.cpp 和 test2.cpp 生成的目标文件时,它会给你一个多重定义错误。如果您将特化标记为inline,那么它们可以在两个 TU 中定义,链接器只会丢弃其中一个定义:

template<>
inline bool Foo_Helper<1>::Compare(const Foo& this_foo, const Foo& other_foo)
//^^^^
{
    return this_foo.Value() == other_foo.Value();
}

template<>
inline bool Foo_Helper<2>::Compare(const Foo& this_foo, const Foo& other_foo)
//^^^^
{
    return this_foo.Value() == other_foo.Value();
}

【讨论】:

  • 啊,这很酷。现在我明白了。 inline 使 TU 单独编译。可能完成了更多的编译,但它解决了问题。
【解决方案2】:

您也可以将专业化代码放在 .cpp 中。我认为这可能是最好的解决方案,因为专业化(通常)与新创建的类(Test1 和 Test2)密切相关。

换一种方式

template<>
bool Foo_Helper<1>::Compare(const Foo& this_foo, const Foo& other_foo)
{
    return this_foo.Value() == other_foo.Value();
}

template<>
bool Foo_Helper<2>::Compare(const Foo& this_foo, const Foo& other_foo)
{
    return this_foo.Value() == other_foo.Value();
}

进入 foo.cpp(而不是 foo.hpp)并且在 foo.hpp 中只说专业化是“extern”。

此外,您应该将类​​声明从 src1.cpp 移动到 src1.hpp ;)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多