Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top