模板函数中本地派生对象的链接错误

发布于 2024-12-19 10:14:13 字数 1221 浏览 4 评论 0原文

我有一个模板化函数,它使用派生自另一个基类的本地类。当此函数在不同的编译单元中实例化时,链接器会为默认构造函数和析构函数抛出“多重定义”错误。

以下是一些给我带来麻烦的代码的精简版本。它由三个文件组成。它应该是有效的(?)C++ 代码:

ah:

struct foo {
    template <typename T>
    void f(const T&);
};

struct base {
    virtual ~base(){};
};

template <typename T>
void foo::f(const T&) {
    struct derived: public base {
      // derived(){}
      // virtual ~derived(){}
    };
    derived x;
}

a.cpp:

#include "a.h"
void fa() {
    foo a;
    a.f(1);
}

int main(int argc, char *argv[]){}

b.cpp:

#include "a.h"
void fb() {
    foo a;
    a.f(1);
}

编译此代码会生成链接器错误,因为导出的构造函数和析构函数存在两次:

$ g++ a.cpp b.cpp
/tmp/ccvPK1l5.o: In function `void foo::f<int>(int const&)::derived::derived()':
b.cpp:(.text+0x24): multiple definition of `void foo::f<int>(int const&)::derived::derived()'
/tmp/ccRb6RYO.o:a.cpp:(.text+0x36): first defined here
[...]

有趣的是,如果您手动定义派生的构造函数和析构函数派生(通过取消注释这两行),一切正常。

我的代码中是否有任何无效内容,或者是 gcc 中的错误?我尝试了 gcc 4.3 和 4.4,两者都有同样的问题。

对于我的真实代码,我通过将“派生”声明为全局类而不是 f 中的本地类来解决这种情况。但我仍然有兴趣知道出了什么问题以及原因,这样我就可以在将来避免它。

I'm having a templated function that uses a local class which is derived from another base class. When this function gets instantiated in different compilation units, the linker throws "multiple definition" errors for the default constructor and destructor.

The following is a boiled down version of some code that was causing me trouble. It consists of three files. It is meant to be valid(?) C++ code:

a.h:

struct foo {
    template <typename T>
    void f(const T&);
};

struct base {
    virtual ~base(){};
};

template <typename T>
void foo::f(const T&) {
    struct derived: public base {
      // derived(){}
      // virtual ~derived(){}
    };
    derived x;
}

a.cpp:

#include "a.h"
void fa() {
    foo a;
    a.f(1);
}

int main(int argc, char *argv[]){}

b.cpp:

#include "a.h"
void fb() {
    foo a;
    a.f(1);
}

Compiling this generates a linker error because the constructor and destructor of derived are there twice:

$ g++ a.cpp b.cpp
/tmp/ccvPK1l5.o: In function `void foo::f<int>(int const&)::derived::derived()':
b.cpp:(.text+0x24): multiple definition of `void foo::f<int>(int const&)::derived::derived()'
/tmp/ccRb6RYO.o:a.cpp:(.text+0x36): first defined here
[...]

Interestingly, if you manually define the constructor and destructor of derived (by uncommenting the two lines), everything works fine.

Is there anything invalid in my code or is it a bug in gcc? I tried gcc 4.3 and 4.4, both are having the same issue.

For my real code I solved the situation by declaring "derived" as a global class as opposed to a local one inside f. But I would still be interesting in knowing what was going wrong and why so I can avoid it in the future.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

她如夕阳 2024-12-26 10:14:13

规范说本地类 (9.8) 的成员函数没有链接。 (C++0x 9.3p3),所以这可能是 gcc 问题。

但是,它似乎在 g++4.5 中得到了解决,因为您的示例成功通过了编译并与 g++ 4.5.2 链接(带或不带注释的构造函数和析构函数):

$ cat a.h
struct foo {
    template <typename T>
    void f(const T&);
};

struct base {
    virtual ~base(){};
};

template <typename T>
void foo::f(const T&) {
    struct derived: public base {
      //derived(){}
      //virtual ~derived(){}
    };
    derived x;
}

$ cat a.cpp
#include "a.h"
void fa() {
    foo a;
    a.f(1);
}

int main(int argc, char *argv[]){}

$ cat b.cpp
#include "a.h"
void fb() {
   foo a;
   a.f(1);
}

$ g++ --std=c++0x --pedantic a.cpp b.cpp -o a
$ g++ -v
Using built-in specs.
COLLECT_GCC=/usr/bin/g++
COLLECT_LTO_WRAPPER=/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.5.2-8ubuntu4' --with-bugurl=file:///usr/share/doc/gcc-4.5/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.5 --enable-shared --enable-multiarch --with-multiarch-defaults=x86_64-linux-gnu --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib/x86_64-linux-gnu --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.5 --libdir=/usr/lib/x86_64-linux-gnu --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-plugin --enable-gold --enable-ld=default --with-plugin-ld=ld.gold --enable-objc-gc --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4)

The specification said Member functions of a local class (9.8) have no linkage. (C++0x 9.3p3), so this is probably a gcc problem.

However, it seems to be solved in g++4.5, because you example successfully passed compilation and link with g++ 4.5.2 (with or without the constructor and destructor commented):

$ cat a.h
struct foo {
    template <typename T>
    void f(const T&);
};

struct base {
    virtual ~base(){};
};

template <typename T>
void foo::f(const T&) {
    struct derived: public base {
      //derived(){}
      //virtual ~derived(){}
    };
    derived x;
}

$ cat a.cpp
#include "a.h"
void fa() {
    foo a;
    a.f(1);
}

int main(int argc, char *argv[]){}

$ cat b.cpp
#include "a.h"
void fb() {
   foo a;
   a.f(1);
}

$ g++ --std=c++0x --pedantic a.cpp b.cpp -o a
$ g++ -v
Using built-in specs.
COLLECT_GCC=/usr/bin/g++
COLLECT_LTO_WRAPPER=/usr/lib/x86_64-linux-gnu/gcc/x86_64-linux-gnu/4.5.2/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu/Linaro 4.5.2-8ubuntu4' --with-bugurl=file:///usr/share/doc/gcc-4.5/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.5 --enable-shared --enable-multiarch --with-multiarch-defaults=x86_64-linux-gnu --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib/x86_64-linux-gnu --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.5 --libdir=/usr/lib/x86_64-linux-gnu --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-plugin --enable-gold --enable-ld=default --with-plugin-ld=ld.gold --enable-objc-gc --disable-werror --with-arch-32=i686 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4)
愿与i 2024-12-26 10:14:13

我认为这与模板没有任何关系,因为这通常发生在头文件中定义的函数中。例如...如果您在 ah 中创建了一个函数并且甚至没有使用它...

   int test()
   {
        static int foo=3;
        return foo;
   }

如果您尝试编译它。它会抱怨这是一个多重定义。修复它的方法是添加内联 ie 像

   inline int test()
   {
        static int foo=3;
        return foo;
   }

这也将修复你的情况。要记住的一点是,在头文件中定义并从多个位置包含的函数将在每个翻译单元中进行编译。这意味着当您链接时将会有多个定义。如果您不希望它成为全局函数符号,您可以像上面那样将其内联,或者可以将其设为静态。如果

将其设为静态,代码将出现在使用它的每个目标文件中。如果将其内联,它将(可能)内联到每个函数中。

I don't think this has anything to do with templates, because this often happens with functions that are defined in header files. For example... if you made a function in your a.h and didn't even use it...

   int test()
   {
        static int foo=3;
        return foo;
   }

f you try to compile this. It will complain about this being a multiple definition. The way to fix it is to add inline i.e. like

   inline int test()
   {
        static int foo=3;
        return foo;
   }

This will also fix your case. The point to remember is that a function defined in a header file and included from multiple places will be compiled in each translation unit. That means when you link there will be multiple definitions. If you don't want it to be a global function symbol you can make it inlined like I did above, or you can make it static. If

you make it static the code will appear in every object file that uses it. If you make it inline it will be inlined (potentially) into every function.

鱼忆七猫命九 2024-12-26 10:14:13

aselle上面所说的是一个有趣的解释,尽管示例中的多重定义不是“f”函数,而是“派生”本地类ctor和dtor。无论如何,作为替代解决方法,将“f”模板成员函数声明为内联可以解决 GCC 上的链接问题:

  struct foo {
      template <typename T>
      inline void f(const T&);
  };

What aselle said above is an interesting explanation, although the multiple definition in your example is not the 'f' function, but the 'derived' local class ctor and dtor. Anyway, as an alternative workaround, declaring the 'f' template member function as inline solves the linking problem on GCC:

  struct foo {
      template <typename T>
      inline void f(const T&);
  };
悲欢浪云 2024-12-26 10:14:13

如果您不希望内联定义所有标头定义的函数(这并不总是最好的态度),您可以避免使用预处理器在旧编译器上进行多次包含

#ifndef _A_H_
#define _A_H_
// code of a.h
#endif

,或者仅

#pragma once

在文件顶部进行包含。我正在使用 g++ 4.6 并且您的代码通过编译而没有错误,因此升级也是个好主意。

If you do not wish to define all header-defined functions inline (it is not always the best attitude), you avoid multiple including on older compilers using preprocessor

#ifndef _A_H_
#define _A_H_
// code of a.h
#endif

or alternatively just

#pragma once

on the top of the file. I am using g++ 4.6 and your code passes compilation without errors, so upgrading is also good idea.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文