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?
You can use pointers to call functions and to pass functions as arguments to other functions. You cannot perform pointer arithmetic on pointers to functions.
In plain word, odr-used means something(variable or function) is used in a context where the definition of it must be present.
If an object, a reference or a function is odr-used, its definition must exist somewhere in the program; a violation of that is usually a link-time error. struct S { static const int x = 0; // static data member // a definition outside of class is required if it is odr-used }; const int& f(const int& r); int n = b ? (
It's as if we are declaring a function called *fun_ptr which takes int and returns void . The key to writing the declaration for a function pointer is to think of it as a function declaration, but with *fun_name instead of func_name . The pointer symbol * precedes the declaration of the function pointer.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With