8

There are so many questions on ODR but I cannot find what I'm looking for, so apologies if this a duplicate or if the title is inappropriate.

Consider the following:

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

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

This is an over-simplification of my attempt to define a unique identifier per type, that hopefully remains unique across different compilation units.

In particular, given a concrete type T like std::string, and assuming two distinct compilation units include the above code in a header file, I would like expression

type<T>().id

to take the same value (of type t(*)()) in both units, hence serve as a unique identifier for type T.

The value is the address of function type<T>, so the question is whether a unique function type<T> in the program is guaranteed by the one-definition rule. iso 3.2/3 says

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program.

where by 3.2/2

A non-overloaded function whose name appears as a potentially-evaluated expression or [...], is odr-used, unless [...]

and I assume a function is non-inline if its address is taken (though I can't find that in the standard).

iso 3.2/5 lists a number of exceptions, but the only references to functions are

inline function with external linkage, [...], non-static function template, [...], member function of a class template, or template specialization for which some template parameters are not specified [...]

and none appears to be the case here.

A verifiable example would take more than one file. In fact, an example claimed to fail is given by Dieter Lücking, though it does not fail in my case (which I do not take as any form of "guarantee").

So, is this going to work or not?

Community
  • 1
  • 1
iavr
  • 7,547
  • 1
  • 18
  • 53
  • This is practically the same question: http://stackoverflow.com/questions/7670000/addresses-of-identical-function-template-instantiations-across-compilation-units (But I am not sure the answer given there is entirely accurate). – jogojapan May 02 '14 at 22:54
  • @jogojapan Indeed, the question is practically the same, thanks. But I cannot say the situation is clear to me by looking at the answers. Is `type` a "non-static function template"? So does 3.2/5 apply? – iavr May 02 '14 at 23:25
  • 1
    Well, isn't this a function template? And it is not static, so isn't it a *"non-static function template"*? If it were a static function template, it had internal linkage, so there would be no requirements on the equality of the contents between function templates with the same name in different TUs. – dyp May 02 '14 at 23:47
  • Agree with dyp. I don't see why it wouldn't fall under the non-static function template exception. However, what if you have a static local variable inside it? Doesn't the standard require that be unique? – kec May 03 '14 at 02:34
  • See this: http://stackoverflow.com/questions/994353/static-variable-inside-template-function. – kec May 03 '14 at 02:37
  • I attempted to reproduce Dieter Lucking example on Linux. It fails to fail, because on Linux all templates are defined as weak symbols. These are collapsed at link time into just one version. – kec May 03 '14 at 02:55
  • I read 3.2/5. I think this is a red herring. It's basically saying that the usual way of organizing templates is okay. If you have a function template definition, you can include it in more than one translation unit, and all those can be put together in one program. So it's not saying that in such a case the same instantiation can have different addresses. – kec May 03 '14 at 03:15
  • @dyp I confused "non-static" as referring to member functions, though it apparently refers to linkage. This is so unfortunate terminology... – iavr May 03 '14 at 08:08
  • @user3521733 Ok, so 3.2/5 applies. I read it again and again, but I can't say I really understand :-) Are all its requirements satisfied by the code above? If yes, does "the program shall behave as if there were a single definition of D" also include `D`'s address? – iavr May 03 '14 at 08:20
  • @iavr: Unfortunately I had a slightly longer explanation as an answer, but some reviewer converted it to a comment; but only kept the first paragraph. I'll try again. – kec May 03 '14 at 12:29
  • @iavr: I am puzzled by the Lucking example that you referred to. I left a comment asking for his platform. There is some possibility that he tested on a non-compliant implementation. I tested his example on Linux with g++ 4.9.0, and it worked as you expected. – kec May 03 '14 at 13:31
  • Somewhat related to your last points: [Do distinct functions have distinct addresses?](http://stackoverflow.com/q/26533740/1708801) – Shafik Yaghmour Jul 08 '15 at 12:05
  • Have it been brought up that how this is supposed to work across **dynamically linked** shared library boundary, or it just could not? – xiay Apr 05 '19 at 23:35

1 Answers1

4

So 3.2/5 actually seems like pretty strong support. First note that a definition is a source code construct, not an object code construct, though clearly there is a very close relationship. 3.2/5 is saying that it's okay to have multiple definitions of non-static function templates, and that furthermore in such a case it must behave as if there were only a single definition. If a function had different addresses in different translation units, then that is not behaving as if there were only one definition, at least in my reading.

This is especially true since a function pointer can be passed as a non-type template argument. Such arguments must be constant and must be the same for all translation units. For example, a string literal cannot be such an argument precisely because its address varies across translation units.

Whether or not all the requirements are met will depend exactly on the context of the multiple definitions, since they deal with things such as name resolution, etc. However, they are all "run-of-the-mill" requirements that are of the "of-course" type. For example, a violation of it would be something like:

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;    
}

I know that multiple definitions are supported in Linux via weak symbols. In fact, on Linux the Lucking example fails to fail precisely because of this. I left a comment to his answer asking for platform. At link time, the linker will throw away all instances of a weak symbol except one. Obviously, if the instances aren't actually the same, that would be bad. But those requirements in 3.2/5 are designed to ensure that the instances are in fact all the same and thus the linker can keep only one.

ADDENDUM: Dieter Lucking now says that he had a compilation problem, and it in fact does not fail for him. It would be good if someone familiar with the internals of Windows DLLs could comment here, though, as to how Visual Studio handles this.

kec
  • 2,099
  • 11
  • 17
  • Thanks, I hope you are right in your reading. I would be happier if there was some consensus. Your example is not on how I *use* the code, but on how I *define* it. My definition of `type` only has *itself* as a symbol in the function body. This does not violate the requirements, does it? – iavr May 03 '14 at 14:29
  • Technically, yes, it's on how you define it. I should change my wording and will in a bit. Note that each inclusion of your template in a TU is a separate definition, so you could in theory inadvertently include it in a way that causes the resulting definition to not satisfy the requirement. In your case, though, it seems impossible, though, unless the TU did really malicious things like use macros, define a specialization, etc. Also, I added something in the answer above about Lucking's example. – kec May 03 '14 at 15:01