Note: The original problem heavily uses macros, but it has been simplified for this question.
// header.hpp template <typename T> void foo() { someBoolean = true ; // at this point "someBoolean" wasn't } // declared
Then, it is used in the following source:
// source.cpp #include "header.hpp" static bool someBoolean = false ; void bar() { foo<char>() ; // here, we call/instantiate the function }
In some compilers (Windows, previous Solaris) it works. And in the current C++11 enabled Solaris compiler, it fails, saying someBoolean is undefined.
According to the standard, can a templated code use a variable that will be (we hope!) declared later in the source?
The template is defined in a header that is expected to be included in multiple sources, each one having its own boolean variable, and instantiating the template.
It was expected, in each translation unit, the template would affect the static boolean variable of that translation unit.
Each instantiation of the template on one type (say "char") is thus expected to affect different variables.
Aren't we relying on undefined behavior, there?
There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.
Undeclared: It occurs when we try to access any variable that is not initialized or declared earlier using var or const keyword. If we use 'typeof' operator to get the value of an undeclared variable, we will face the runtime error with return value as “undefined”.
To instantiate a template function explicitly, follow the template keyword by a declaration (not definition) for the function, with the function identifier followed by the template arguments. template float twice<float>( float original ); Template arguments may be omitted when the compiler can infer them.
The template function works for int and char, but not float and string.
This has to do with two-phase lookup. The short version is that any names that do not depend on the template parameter (like someBoolean
here) will be looked up at template definition time. That means that the Solaris compiler is correct in rejecting the code. someBoolean
has not been defined before the template is defined.
Names that do depend on the template parameter (e.g. if you had written something like T::someBoolean = true
) will be deferred to template instantiation time - very reasonably, since their validity can't be determined until the compiler knows what T
is. MSVC is known for not implementing these two-phase semantics properly (at least historically), which is why your code works there. It is not correct C++ and not portable behaviour, though.
Section 14.6 of (unknown version of) the C++ standard (probably draft):
If a name does not depend on a template-parameter (as defined in 14.6.2), a declaration (or set of declarations) for that name shall be in scope at the point where the name appears in the template definition; the name is bound to the declaration (or declarations) found at that point and this binding is not affected by declarations that are visible at the point of instantiation.
(via @BenVoigt comment below)
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