2012-03-20 100 views
0

我有一组我的头声明这样的功能:具有相同名称和签名的多个功能相互覆盖?

actual_function.hpp

#ifndef ACTUAL_FUNCTION_HPP 
#define ACTUAL_FUNCTION_HPP 

#include <iostream> 

#ifdef CONDITION 
#warning Compiling version 1 
template<typename T> 
T fun (T x) { 
    std::cout << "Version 1 implementation is called.\n"; 
    return x + x; 
} 
#else 
#warning Compiling version 2 
template<typename T> 
T fun (T x) { 
    std::cout << "Version 2 implementation is called.\n"; 
    return 2 * x + 1; 
} 
#endif 

#endif 

我想在一个测试程序来测试该函数的两个版本。我以为我可以与多个翻译单位做到这一点,所以我有一个文件布局是这样的:

main.cpp中:

void test_version_1(); 
void test_version_2(); 
int main() { 
    test_version_1(); 
    test_version_2(); 
    return 0; 
} 

test1.cpp:

#include <cassert> 
#include <iostream> 
#define CONDITION 
#include "actual_function.hpp" 
void test_version_1() { 
    std::cout << "Version 1 is called.\n"; 
    assert (fun (8) == 16); 
} 

测试2.cpp

#include <cassert> 
#include <iostream> 
#undef CONDITION 
#include "actual_function.hpp" 
void test_version_2() { 
    std::cout << "Version 2 is called.\n"; 
    assert (fun (8) == 17); 
} 

我的想法是,这会给test1.cpp版本1的乐趣和test2.cpp版本2的乐趣。预处理器输出似乎支持这种想法:

g++ main.cpp test1.cpp test2.cpp 
In file included from test1.cpp:4:0: 
actual_function.hpp:7:2: warning: #warning Compiling version 1 [-Wcpp] 
In file included from test2.cpp:4:0: 
actual_function.hpp:14:2: warning: #warning Compiling version 2 [-Wcpp] 

但是,我的猜测是,链接器混合了我的东西。当我运行该程序,这是发生了什么:

./a.out 
Version 1 is called. 
Version 1 implementation is called. 
Version 2 is called. 
Version 1 implementation is called. 
a.out: test2.cpp:7: void test_version_2(): Assertion `fun (8) == 17' failed. 
Aborted (core dumped) 

如果我只在定义一个命名乐趣别的东西,并调用新命名的功能,一切正常,这表明了正确的功能在正确的位置可见。如果我只在定义处重命名函数,但不改变调用点,则会出现编译器错误test2.cpp:7:2: error: ‘fun’ was not declared in this scope。这使我认为链接器覆盖了函数,因为它们具有相同的名称和签名。

这是真的吗?如果是这样,最好的解决方案是什么?我的两个想法如下:

1:我的函数需要一个额外的模板参数,所以它会是模板,然后专注于真正与错误。实际上,我可能需要一些比这更复杂的东西(也许专注于一个int或某物),因为我真正的问题有更多的选择。如果定义了CONDITION宏,它将使用手动版本。如果条件宏没有被定义,那么它会查看它是否知道我手动执行的任何编译器内在函数,如果是,它就会使用它们,否则,无论宏是否存在,它都会回到手动定义。不过,某些模板专业化仍然可以在这里工作。

2:使用不同名称创建函数fun_manualfun_intrinsic,并且fun是一个包装函数,它根据其名称调用它们。我不完全确定这将如何工作。

我主要关心的是,如果编译器不支持内部版本,编译器就无法看到内部版本,否则会给出错误。

我的两个解决方案是我能做的最好的,还是有更好的?

+1

这是未定义的行为,如果不是所有的翻译单位看到完全相同的定义。 – 2012-03-20 22:37:46

+2

你有功能模板,而不是功能。 – 2012-03-20 22:39:01

+0

GCC中是否有警告可以打开以提醒用户多个功能正在合并为一个?修好我的'actual_function.hpp'头后,我仍然得到了同样的错误结果。然后我意识到,在我的代码的另一部分,我忘记了在未命名的名称空间中粘贴一些函数。 – 2012-03-20 23:43:27

回答

5

您正在违反One Definition Rule。基本上,编译器和链接器的组合允许将功能的不同版本视为相同。

0

我的最终解决方案,接着我的想法#2,和看起来是这样的:

#undef NO_INTRINSICS 
#undef FUNCTION 
// INTRINSIC_VERSION_?_EXISTS are a set of conditional statements that are 
// defined by my environment 
#if INTRINSIC_VERSION_X_EXISTS 
    #define FUNCTION INTRINSIC_FUNCTION_X 
#elif INTRINSIC_VERSION_Y_EXISTS 
    #define FUNCTION INTRINSIC_FUNCTION_Y 
#else 
    #define NO_INTRINSICS 
#endif 

template<typename T> 
T manual_function (T x) { 
    // implementation 
} 
// Compilation will fail if the user requests intrinsic versions but none 
// exist. 
template<typename T> 
T intrinsic_function (T x) { 
    return FUNCTION (x); 
} 
template<typename T> 
T function (T x) { 
    #if defined NO_INTRINSICS 
     return manual_function (x); 
    #else 
     return intrinsic_function (x); 
    #endif 
} 

#undef FUNCTION 
#undef NO_INTRINSICS 

这让我来测试我的职务说明书和内在的版本。但是,并不总是内在版本可用,所以用户可以安全地调用函数,如果函数存在(为了速度),将选择内部版本,否则就回到手动版本(为了便携性)。

我通过简单地调用manual_function来测试手动版本。我通过调用函数来测试内部版本(因此,如果在特定平台上没有内部版本,则没有编译错误)。很明显,如果没有内部版本,我并不是真的通过调用函数来测试任何新东西,但那是因为没有什么可以测试的,这意味着我的测试仍然具有完整的覆盖范围。