指向函数和ODR的指针

时间:2014-05-02 22:36:42

标签: c++ c++11 function-pointers function-templates one-definition-rule

关于ODR有很多问题,但我找不到我要找的东西,如果这个副本或标题不合适,请道歉。

请考虑以下事项:

struct t {t(*id)();};

template<typename T>
t type() {return {type<T>};}

这是我对定义unique identifier per type的尝试的过度简化,希望在不同的编译单元中保持唯一。

特别是,给定一个类似T的具体类型std::string,假设两个不同的编译单元在头文件中包含上述代码,我想表达式

type<T>().id

在两个单元中采用相同的值(类型t(*)()),因此用作类型T的唯一标识符。

该值是函数type<T>的地址,因此问题是程序中的唯一函数type<T>是否由one-definition rule保证。 iso 3.2 / 3说

  

每个程序应该只包含该程序中使用的每个非内联函数或变量的一个定义。

其中3.2 / 2

  

除非[...]

,否则名称显示为可能已评估的表达式或[...]的非重载函数将被使用。

我假设一个函数是非内联的,如果它的地址被采用(虽然我在标准中找不到它)。

iso 3.2 / 5列出了许多例外,但对函数的唯一引用是

  

具有外部链接的内联函数,[...],非静态函数模板,类模板的成员函数或未指定某些模板参数的模板特化[...]

并且在这里似乎没有。

可验证的示例需要多个文件。事实上,Dieter Lücking给出了一个声称失败的例子,虽然它在我的情况下没有失败(我没有采取任何形式的“保证”)。

那么,这是否会起作用?

1 个答案:

答案 0 :(得分:4)

所以3.2 / 5实际上看起来非常强大。首先请注意,定义是源代码构造,而不是目标代码构造,但显然存在非常密切的关系。 3.2 / 5表示可以对非静态函数模板进行多种定义,而且在这种情况下,它必须表现得好像只有一个定义。如果一个函数在不同的翻译单元中有不同的地址,那么就不会表现得好像只有一个定义,至少在我的阅读中。

由于函数指针可以作为非类型模板参数传递,因此尤其如此。这些参数必须是常量,并且对于所有翻译单元必须相同。例如,字符串文字不能正是这样的参数,因为它的地址因翻译单元而异。

是否满足所有要求将完全取决于多个定义的上下文,因为它们处理诸如名称解析等内容。但是,它们都是“普通的”要求,属于“当然”类型。例如,违反它就像是:

file1.cpp

static int i;

// This is your template.
template <typename T>
void foo() {
    i; // Matches the above i.
}

file2.cpp

static int i;

// This is your template. You are normally allowed to have multiple
// identical definitions of it.
template <typename T>
void foo() {
    // Oops, matches a different entity. You didn't satisfy the requirements.
    // All bets are off.
    i;    
}

我知道Linux中通过弱符号支持多个定义。实际上,在Linux上,Lucking的例子并没有因此而失败。我对他的答案提出了评论,要求提供平台。在链接时,链接器将丢弃除弱符号之外的所有弱符号实例。显然,如果实例实际上并不相同,那就太糟糕了。但3.2 / 5中的那些要求旨在确保实例实际上完全相同,因此链接器只能保留一个。

ADDENDUM:Dieter Lucking现在说他有编译问题,事实上他并没有失败。如果熟悉Windows DLL内部的人可以在这里评论,那将是很好的,关于Visual Studio如何处理这个问题。