【问题标题】:Link error with really simple functions C++ on .h file链接错误与.h文件上的非常简单的函数C++
【发布时间】:2011-06-21 12:03:03
【问题描述】:

我制作了两个函数将 32/64 位指针“转换”为双精度。该代码在单独使用时有效(仅 .h 和 .cpp 包括它),但是当在其他地方使用 .h 时(复制到项目目录中然后包含),对于 . h 文件时链接。

.h 文件的源代码如下:

#pragma once
#ifndef __FLOATCAST_H
#define __FLOATCAST_H

//A quick and dirty way of casting pointers into doubles and back
//Should work with BOTH 64bit and 32bit pointers
union ptr_u {
    double d;
    void* p;
};

double ptr2double(void* pv){
    ptr_u ptr;
    ptr.p = pv;
    return (ptr.d);
};

void* double2ptr(double dv){
    ptr_u ptr;
    ptr.d = dv;
    return(ptr.p);
};

#endif

链接说函数已经在一个文件源文件(而不是他的 .obj)上定义,它不包括这个。

编辑: 为什么我想要一个指针内双?因为我需要 Lua (5.1) 来回调一个对象成员函数。

编辑2: Lua 提供了一种存储用户数据的方法,这似乎是一种足够的解决方案,而不是转换指针(参见 cmets)

【问题讨论】:

  • 将指针指向 double 听起来像是一场等待发生的灾难。在 Lua 中查找“轻量级用户数据”。
  • 我刚刚开始使用 Lua C api,但它只是飞过我。我会看看,因为这似乎是真正的解决方案。 (我知道双重施法是个笨蛋,但它奏效了;)
  • 同意@interjay - 只有“用户数据”

标签: c++ visual-studio linker compiler-errors


【解决方案1】:

内联标记你的函数

inline double ptr2double(void* pv){
    ptr_u ptr;
    ptr.p = pv;
    return (ptr.d);
};

inline void* double2ptr(double dv){
    ptr_u ptr;
    ptr.d = dv;
    return(ptr.p);
};

您会看到,当您在两个单独的源文件(翻译单元)中包含相同的函数时,您会得到多个定义。您通常有两种选择:

  • 在 .h 文件中定义函数,在单独的 .cpp 文件中定义函数
  • 内联函数并将它们保存在 .h 文件中

C++ 的单一定义规则禁止非内联函数的多重定义。

编辑: #ifdef 可防止在单个 源文件中包含多个内容。但是您确实可以将 .h 文件包含到不同的 .cpp 文件中。 ODR 适用于整个程序中的定义,而不仅仅是单个文件。

EDIT2 在一些 cmet 之后,我觉得我必须在此处合并这条信息,以免产生任何误解。在 C++ 中,关于内联函数和非内联函数有不同的规则,例如 ODR 的特殊情况。现在,您可以将任何函数(无论是长函数还是递归函数)标记为内联,特殊规则将适用于它们。编译器是否会决定实际内联它(即替换代码而不是调用)是完全不同的事情,即使您没有将函数标记为内联,它也可以这样做,并且可以决定不这样做即使您将其标记为内联。

【讨论】:

  • 成功了,但是为什么呢? .h 内容受#ifdef preprocesor 命令保护。如果我不能内联函数怎么办? (即递归)编辑:我假设我需要一个单独的 .cpp
  • 正如 interjay 在其他答案中所说,STL(和我制作的模板)确实在 .h 中有代码,但不需要内联。
  • @NeonMan:除了非模板之外,模板还有其他规则。大多数STL代码是类模板和函数模板
  • 内联与递归有什么关系?但是如果你不能让它内联,就按照他的建议去做。
  • @RedX:您可以将任何函数标记为内联 - 无论它是否递归。您可以并且应该有多个定义不管函数是否会被编译器实际内联
【解决方案2】:

规范的方法是:

floatcast.h

#pragma once
#ifndef __FLOATCAST_H
#define __FLOATCAST_H

//A quick and dirty way of casting pointers into doubles and back
//Should work with BOTH 64bit and 32bit pointers
union ptr_u {
    double d;
    void* p;
};

double ptr2double(void* pv);
void* double2ptr(double dv);

#endif

floatcast.cpp

#include "floatcast.h"

double ptr2double(void* pv){
    ptr_u ptr;
    ptr.p = pv;
    return (ptr.d);
};

void* double2ptr(double dv){
    ptr_u ptr;
    ptr.d = dv;
    return(ptr.p);
};    

【讨论】:

    【解决方案3】:

    将您的代码放入 .cpp 文件中,然后在 .h 文件中放入原型 -

    double ptr2double(void* pv);
    void* double2ptr(double dv);
    

    或者保持代码不变并在每个函数定义中添加“内联”,这样您就可以在每个模块中定义它们

    虽然我认为您的代码实际上不会做任何非常有用的事情。 访问联合的不同成员而不是用于存储数据的成员是未定义的行为。另外,即使那个“有效”我想不出你打算如何使用它,你会存储一个 double 并将它的 32 位读入一个指针(在 32 位机器上)......你怎么可能利用这个有什么用?

    【讨论】:

    • 由于 lua 唯一的数字类型是双精度,因此传递指针以供 Lua 回调(解释很长)的工作方法
    • 啊,好吧,您只是将指针存储在双精度中,而不是相反。仍然很狡猾,但可能在实践中起作用。 lua 没有一些“用户数据”的概念来存储这种东西吗?我已经很久没有用它做了任何事情了。
    • 将 ptr 转换为 double 是危险的,Lua 有“用户数据” - 在上方几厘米处张贴
    【解决方案4】:

    您收到错误是因为函数被编译了两次(每次都包含头文件),因此将所有目标文件组合到最终可执行文件中的链接器会看到函数的多个定义。

    就像 Armen 写的那样,一种解决方案是使函数内联。这样,函数的代码在使用时总是被复制。这是一个可行的解决方案,因为函数很小,代码不会变得很臃肿。如果您在头文件中有任何更大的函数,或者您需要更好地组织代码,您应该从头文件中获取代码并放入 cpp 文件中(如 sehe 的答案)。

    【讨论】:

      猜你喜欢
      • 2011-03-22
      • 1970-01-01
      • 2015-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多