In the following example, Foo
is not doing what is intended, but I can't figure out why this is allowed to compile.
#include <string>
#include <iostream>
typedef std::string& T;
T Foo(int & i)
{
return T(i);
}
int main()
{
int a = 1;
std::string & s = Foo(a);
}
I discovered this with templates, but the typedef
shows that its unrelated to templates. Needless to say, s
is not a valid string here. I would think that constructing the value in the return of Foo
would produce a compile error.
What am I missing here?
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
For example, given a specialization Stack<int>, “int” is a template argument. Instantiation: This is when the compiler generates a regular class, method, or function by substituting each of the template's parameters with a concrete type.
A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument. A non-type parameter can be any of the following types: An integral type. An enumeration type. A pointer or reference to a class object.
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
First of all, it worth nothing that the problem actually has no relationship to templates because this code compiles a well:
typedef std::string& T;
T Foo(int& i) {
return T(i);
}
The reason I think this compiles is that the return
statement is equivalent to
return reinterpret_cast<T>(i);
in case T
happens to be a reference members. ... and this, of course, compiles: You promised you knew what you were doing and asked the compiler kindly to believe you.
OK, found it at 5.2.3 [expr.type.conv] paragraph 1:
... If the expression list is a single expression, the type conversion expression is equivalent (in definedness, and if defined in meaning) to the corresponding cast expression (5.4). ...
... and 5.4 [expr.cast] paragraph 4:
The conversions performed by [other forms of casts] a reinterpret_cast (5.2.10) [...] can be performed using the cast notation of explicit type conversion. [...]
(the elisions cover cases involving user defined type, built-in type conversions, const
conversions, etc.)
This has nothing to do with templates, you get the same result if T
is just a typedef for std::string&
rather than a deduced template parameter:
#include <string>
typedef std::string& T;
T Foo(int & i)
{
return T(i);
}
int main()
{
int a = 1;
std::string & s = Foo(a);
}
Dietmar's answer made me realise this can be further simplified to:
#include <string>
typedef std::string& T;
int main()
{
int a = 1;
std::string & s = T(a);
}
where T(a)
is the same as the cast (T)a
i.e. (std::string&)a
which (according to the rules of 5.4 [expr.cast]) will do a const_cast
if that's valid (which it isn't) or a static_cast
if that's valid (which it isn't) or a static_cast
followed by a const_cast
if that's valid (which it isn't) or a reinterpret_cast
if that's valid (which it is) or a reinterpret_cast
followed by a const_cast
if that's valid, otherwise the expression is ill-formed.
So as Dietmar said, it's the same as doing a reinterpret_cast
, i.e.
std::string & s = reinterpret_cast<std::string&>(a);
I find it quite surprising that the original code compiles, but as it's the same as that line above, it's allowed to compile. Using the result of the cast is undefined behaviour though.
To avoid the surprise where T(a)
is equivalent to a cast, use the new C++11 uniform initialization syntax, T{a}
, which is always an initialization, not a cast expression.
Great question, investigating and answering it showed me a new gotcha I wasn't previously aware of, thanks to JaredC and Dietmar for the new piece of knowledge!
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