Using reference as template type - c++

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?

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!

Related

Why is a cast operator to std::optional ignored?

This code
#include <iostream>
#include <optional>
struct foo
{
explicit operator std::optional<int>() {
return std::optional<int>( 1 );
}
explicit operator int() {
return 2;
}
};
int main()
{
foo my_foo;
std::optional<int> my_opt( my_foo );
std::cout << "constructor: " << my_opt.value() << std::endl;
my_opt = static_cast<std::optional<int>>(my_foo);
std::cout << "static_cast: " << my_opt.value() << std::endl;
}
produces the following output
constructor: 2
static_cast: 2
in Clang 4.0.0 and in MSVC 2017 (15.3). (Let's ignore GCC for now, since it's behavior seems to be buggy in that case.)
Why is the output 2? I would expect 1. The constructors of std::optional seem to prefer casting to the inner type (int) despite the fact that a cast to the outer type (std::optional<int>) is available. Is this correct according to the C++ standard? If so, is there a reason the standard does not dictate to prefer an attempt to cast to the outer type? I would find this more reasonable and could imagine it to be implemented using enable_if and is_convertible to disable the ctor if a conversion to the outer type is possible. Otherwise every cast operator to std::optional<T> in a user class - even though it is a perfect match - would be ignored on principle if there is also one to T. I would find this quite obnoxious.
I posted a somewhat similar question yesterday but probably did not state my problem accurately, since the resulting discussion was more about the GCC bug. That's why I am asking again more explicitly here.
In case Barry's excellent answer still isn't clear, here's my version, hope it helps.
The biggest question is why isn't the user-defined conversion to optional<int> preferred in direct initialization:
std::optional<int> my_opt(my_foo);
After all, there is a constructor optional<int>(optional<int>&&) and a user-defined conversion of my_foo to optional<int>.
The reason is the template<typename U> optional(U&&) constructor template, which is supposed to activate when T (int) is constructible from U and U is neither std::in_place_t nor optional<T>, and direct-initialize T from it. And so it does, stamping out optional(foo&).
The final generated optional<int> looks something like:
class optional<int> {
. . .
int value_;
. . .
optional(optional&& rhs);
optional(foo& rhs) : value_(rhs) {}
. . .
optional(optional&&) requires a user-defined conversion whereas optional(foo&) is an exact match for my_foo. So it wins, and direct-initializes int from my_foo. Only at this point is operator int() selected as a better match to initialize an int. The result thus becomes 2.
2) In case of my_opt = static_cast<std::optional<int>>(my_foo), although it sounds like "initialize my_opt as-if it was std::optional<int>", it actually means "create a temporary std::optional<int> from my_foo and move-assign from that" as described in [expr.static.cast]/4:
If T is a reference type, the effect is the same as performing the
declaration and initializationT t(e); for some invented temporary
variable t ([dcl.init]) and then using the temporary variable as the
result of the conversion. Otherwise, the result object is
direct-initialized from e.
So it becomes:
my_opt = std::optional<int>(my_foo);
And we're back to the previous situation; my_opt is subsequently initialized from a temporary optional, already holding a 2.
The issue of overloading on forwarding references is well-known. Scott Myers in his book Effective Modern C++ in Chapter 26 talks extensively about why it is a bad idea to overload on "universal references". Such templates will tirelessly stamp out whatever the type you throw at them, which will overshadow everything and anything that is not an exact match. So I'm surprised the committee chose this route.
As to the reason why it is like this, in the proposal N3793 and in the standard until Nov 15, 2016 it was indeed
optional(const T& v);
optional(T&& v);
But then as part of LWG defect 2451 it got changed to
template <class U = T> optional(U&& v);
With the following rationale:
Code such as the following is currently ill-formed (thanks to STL for
the compelling example):
optional<string> opt_str = "meow";
This is because it would require two user-defined conversions (from
const char* to string, and from string to optional<string>) where the
language permits only one. This is likely to be a surprise and an
inconvenience for users.
optional<T> should be implicitly convertible from any U that is
implicitly convertible to T. This can be implemented as a non-explicit
constructor template optional(U&&), which is enabled via SFINAE only
if is_convertible_v<U, T> and is_constructible_v<T, U>, plus any
additional conditions needed to avoid ambiguity with other
constructors...
In the end I think it's OK that T is ranked higher than optional<T>, after all it's a rather unusual choice between something that may have a value and the value.
Performance-wise it is also beneficial to initialize from T rather than from another optional<T>. An optional is typically implemented as:
template<typename T>
struct optional {
union
{
char dummy;
T value;
};
bool has_value;
};
So initializing it from optional<T>& would look something like
optional<T>::optional(const optional<T>& rhs) {
has_value = rhs.has_value;
if (has_value) {
value = rhs.value;
}
}
Whereas initializing from T& would require less steps:
optional<T>::optional(const T& t) {
value = t;
has_value = true;
}
A static_cast is valid if there is an implicit conversion sequence from the expression to the desired type, and the resulting object is direct-initialized from the expression. So writing:
my_opt = static_cast<std::optional<int>>(my_foo);
Follows the same steps as doing:
std::optional<int> __tmp(my_foo); // direct-initialize the resulting
// object from the expression
my_opt = std::move(__tmp); // the result of the cast is a prvalue, so move
And once we get to construction, we follow the same steps as my previous answer, enumerating the constructors, which ends up selecting the constructor template, which uses operator int().

Result of decltype in const methods

The C++11 decltype returns the type of the expression given to it (mostly). But this can differ from the type of the expression as it is actually accessible:
template<typename T>
struct Ref {
Ref(T&) { }
};
#define GETTYPE decltype
//#define GETTYPE typeof
struct Problem {
void doit_c() const { Ref<GETTYPE(n)> rn{n}; }
void doit_nc() { Ref<GETTYPE(n)> rn{n}; }
int n;
};
int main() {
int i;
const int ci = 0;
Problem pr;
// decltype == typeof == int
Ref<GETTYPE(i)> ri{i};
pr.doit_nc();
// decltype == typeof == const int
Ref<GETTYPE(ci)> rci{ci};
Ref<GETTYPE(static_cast<const int&>(i))> rcci{static_cast<const int&>(i)};
// typeof == const int, decltype == int (!)
pr.doit_c();
return 0;
}
In the example, the Ref struct is just used to cause a compile error if T does not match the actual constructor argument. The Problem::doit_c() method is where decltype(n) returns a non-const result, even though n is const in this context.
If one switches from the standard decltype to the GNU extension typeof, this seems to take the const-ness of the method into account.
Now my question: Is there a C++11 / C++14 / C++17 compliant alternative to decltype() / typeof() that behaves "correctly" (as in: no compile error above) for expressions like the declaration in the const-method above?
Edited:
Simplified the first sentence to remove some errors and stop distracting from the point of the question (thanks, #skypjack)
Simplified the use use of macros in the example code (thanks, #Richard Critten)
decltype is a feature that kinda sits at two chairs at once. Firstly, as the name suggests, it can give you the exact declared type of an entity, ignoring the context in which it is used. Secondly, it can treat its argument as an expression, whose exact type depends on the context and its value category.
decltype applied directly to a "naked" (unparenthesized) class member access is a special case, in which decltype acts in accordance with its first role. It will not treat n as an expression. Instead it will produce the type of that class member, ignoring the context.
If you want it to treat n as an expression, you have to parenthesize it
struct Problem {
void doit_c() const
{
Ref<decltype(n)> rn1{n}; // `decltype(n)` is `int` -> ERROR
Ref<decltype((n))> rn2{n}; // `decltype((n))` is `const int &` -> compiles OK
}
};
You can find out the effective cv-qualified type of n using a temporary reference:
void doit_c() const { auto& x = n; Ref<GETTYPE(x)> rn{n}; }
void doit_nc() { auto& x = n; Ref<GETTYPE(x)> rn{n}; }
But it's simpler and clearer to parenthesize, as shown in AnT's answer.

Brace-initialization vs. Parenthesis Bug

I was filing a GCC bug for this, but I'd rather double-check this.
Consider the following programs:
#include <utility>
template<typename T, typename A>
void F(A&& a) { T(std::forward<A>(a)); } // Note: () syntax.
int main() { int i; F<int&>(i); }
and:
#include <utility>
template<typename T, typename A>
void F(A&& a) { T{std::forward<A>(a)}; } // Note: {} syntax.
int main() { int i; F<int&>(i); }
Latest Clang and MSVC compilers accept both programs. GCC 5 and beyond accept the first program but reject the second, claiming invalid cast of an rvalue expression of type 'int' to type 'int&'.
Is this a GCC bug? Or is this indeed a difference between T{} and T() in the above context (and thus a bug in Clang and MSVC)?
Edit:
The issue can be narrowed down to the following simpler excerpts:
int i; (int&){i};
and
int i; (int&)(i);
There are two separate issues:
The standard is unclear what T{x} should do for reference type T. Currently [expr.type.conv]/1 says that it creates a prvalue of type T, which is nonsense for reference types. This is core issue 1521.
The sane thing is probably to have T{x} for reference type T do roughly T __tmp{x}; and then yield the equivalent of static_cast<T>(__tmp) (so xvalue for rvalue reference T and lvalue for lvalue reference T). However, C++11 as published screwed up the specification for list-initialization of references, making it always create a temporary. The result was that int i; int &r{i}; failed to compile because it would attempt to bind r to a temporary copy of i, which is obviously nonsense. This is fixed by core issue 1288, whose resolution GCC is supposed to implement, but it looks like from the error message that it's not completely fixed.

constexpr member functions that don't use this?

Please consider the following two C++14 programs:
Program 1:
struct S { constexpr int f() const { return 42; } };
S s;
int main() { constexpr int x = s.f(); return x; }
Program 2:
struct S { constexpr int f() const { return 42; } };
int g(S s) { constexpr int x = s.f(); return x; }
int main() { S s; return g(s); }
Are neither, either or both of these programs ill-formed?
Why/why not?
Both programs are well-formed. The C++14 standard requires that s.f() be a constant expression because it is being used to initialize a constexpr variable, and in fact it is a core constant expression because there's no reason for it not to be. The reasons that an expression might not be a core constant expression are listed in section 5.19 p2. In particular, it states that the evaluation of the expression would have to do one of several things, none of which are done in your examples.
This may be surprising since, in some contexts, passing a non-constant expression to a constexpr function can cause the result to be a non-constant expression even if the argument isn't used. For example:
constexpr int f(int) { return 42; }
int main()
{
int x = 5;
constexpr auto y = f(x); // ill-formed
}
However, the reason this is ill-formed is because of the lvalue-to-rvalue conversion of a non-constant expression, which is one of the things that the evaluation of the expression is not allowed to do. An lvalue-to-rvalue conversion doesn't occur in the case of calling s.f().
I can't seem to find a compelling passage or example in the standard that directly addresses the issue of calling a constexpr member function on a non-constexpr instance, but here are some that may be of help (from draft N4140):
[C++14: 7.1.5/5]:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting
constexpr constructor, if no argument values exist such that an invocation of the function or constructor
could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no
diagnostic required.
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
From this I take that the program is not outright ill-formed just because a constexpr function has a possible non-constexpr path.
[C++14: 5.19]:
int x; // not constant
struct A {
constexpr A(bool b) : m(b?42:x) { }
int m;
};
constexpr int v = A(true).m; // OK: constructor call initializes
// m with the value 42
constexpr int w = A(false).m; // error: initializer for m is
// x, which is non-constant
This is somewhat closer to your example programs, here a constexpr constructor may reference a non-constexpr variable depending on the value of the argument, but there is no error if this path is not actually taken.
So I don't think either program you presented should be ill-formed, but I cannot offer convincing proof :)
This sounds like a quiz question, and not presented by a student, but the professor testing the public on stackoverflow, but let's see...
Let's start with the One Definition Rule. It's clear neither version violates that, so they both pass that part.
Then, to syntax. Neither have syntax failures, they'll both compile without issue if you don't mind the potential blend of a syntax and semantic issue.
First, the simpler semantic issue. This isn't a syntax problem, but f(), in both versions, is the member of a struct, and the function clearly makes no change to the owning struct, it's returning a constant. Although the function is declared constexpr, it is not declared as const, which means if there were some reason to call this as a runtime function, it would generate an error if that attempt were made on a const S. That affects both versions.
Now, the potentially ambiguous return g(S()); Clearly the outer g is a function call, but S may not be so clear as it would be if written return g(S{}); With {} initializing S, there would be no ambiguity in the future should struct S be expanded with an operator() (the struct nearly resembles a functor already). The constructor invoked is automatically generated now, and there is no operator() to create confusion for the compiler at this version, but modern C++14 is supposed to offer clearer alternatives to avoid the "Most Vexing Parse", which g(S()) resembles.
So, I'd have to say that based on semantic rules, they both fail (not so badly though).

Binding lvalue to rvalue reference -- g++ bug?

As an answer to another question I wanted to post the following code (that is, I wanted to post code based on this idea):
#include <iostream>
#include <utility> // std::is_same, std::enable_if
using namespace std;
template< class Type >
struct Boxed
{
Type value;
template< class Arg >
Boxed(
Arg const& v,
typename enable_if< is_same< Type, Arg >::value, Arg >::type* = 0
)
: value( v )
{
wcout << "Generic!" << endl;
}
Boxed( Type&& v ): value( move( v ) )
{
wcout << "Rvalue!" << endl;
}
};
void function( Boxed< int > v ) {}
int main()
{
int i = 5;
function( i ); //<- this is acceptable
char c = 'a';
function( c ); //<- I would NOT like this to compile
}
However, while MSVC 11.0 chokes at the last call, as it IHMO should, MinGW g++ 4.7.1 just accepts it, and invokes the constructor with rvalue reference formal argument.
It looks to me as if an lvalue is bound to an rvalue reference. A glib answer could be that the lvalue is converted to rvalue. But the question is, is this a compiler bug, and if it’s not, how does the Holy Standard permit this?
EDIT: I managed to reduce it all to the following pretty short example:
void foo( double&& ) {}
int main()
{
char ch = '!';
foo( ch );
}
Fails to compile with MSVC 11.0, does compile with MinGW 4.7.1, which is right?
I haven't check the spec but I guess char can be automatically cast to int. Since you cannot assign anything (it's r-value) the R-value to temporary variable of type int (to be more explicit to (int)c value) will be passed.
I discovered that N3290 (identical to C++11 standard) contains non-normative example of binding double&& to rvalue generated from int lvalue, and the updated wording in §8.5.3
“If T1 is reference-related to T2 and the reference is an rvalue reference,
the initializer expression shall not be an lvalue.”
The rules were reportedly designed to avoid inefficient extra copying. Although I fail to see how such copying could not be optimized away. Anyway, whether the rationale is reasonable or not – and it certainly doesn't seem as a reasonable effect! – the following code is allowed, and compiles with both MSVC 11 and MinGW g++ 4.7:
struct Foo {};
struct Bar { Bar( Foo ) {} };
void ugh( Bar&& ) {}
int main()
{
Foo o;
ugh( o );
}
So apparently MSVC 11 is wrong in not permitting the lvalue -> rvalue conversion.
EDIT: I learned that there is Defect Report about this issue, DR 1414. The Feb 2012 conclusion was that the current behavior specification is “correct”, presumably with respect to how well it reflects the intention. It is however reportedly still discussed in the committee, presumably with respect to the practicality of the intention.
Presumably you agree this is valid?
void foo( double ) {} // pass-by-value
int main()
{
char ch = '!';
foo( ch );
}
There's an implicit conversion from char to double, so the function is viable.
It's the same in the example in your edited question, there's an implicit conversion that produces a temporary (i.e. an rvalue) and the rvalue-reference argument binds to that temporary. You can make that conversion explicit if you prefer:
void foo( double&& ) {} // pass-by-reference
int main()
{
char ch = '!';
foo( double(ch) );
}
but that doesn't really change anything in this case. That would be necessary if double -> char could only be converted explicitly (e.g. for class types with explicit constructors or explicit conversion operators) but double to char is a valid implicit conversion.
The "an rvalue-reference cannot bind to an lvalue" rule you're thinking of refers to binding a T&& to a T lvalue, and that rule isn't broken because the double&& doesn't bind to the char, it binds to a temporary created by the implicit conversion.
That rule doesn't only exist to prevent unnecessary extra copying, but to fix a real safety problem that existed with the previous rules, see http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2812.html
Edit: It was asked whether this behaviour is desirable on the committee reflector (see DR 1414) and it was decided that yes, this behaviour is intended and is correct. One of the arguments used to reach that position was that this code is more efficient with the current rules:
std::vector<std::string> v;
v.push_back("text");
With the current rules a temporary std::string is created by an implicit conversion, then std::vector<T>::push_back(T&&) is called, and the temporary is moved into the vector. If that push_back overload Wasn't viable for the result of a conversion then the code above would call std::vector<T>::push_back(const T&) which would cause a copy. The current rules make this real-world use case more efficient. If the rules said rvalue-refs cannot bind to the result of implicit conversions you would have to change the code above to get the efficiency of a move:
v.push_back( std::string{"text"} );
IMHO it makes no sense to have to explicitly construct a std::string when that constructor is not explicit. I want consistent behaviour from explicit/implicit constructors, and I want the first push_back example to be more efficient.