Brace-initialization vs. Parenthesis Bug - c++

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.

Related

Why does this incorrect std::function initialization compile using MSVC?

Came across an interesting issue today started by my own typo. I created a lambda that takes in a reference to a struct and incorrectly set it to a std::function that receives it's argument by value.
Here's a more concise version:
#include <functional>
struct InputStruct
{
int i;
InputStruct(): i(1){}
};
void function_rcv(std::function<bool(InputStruct)> & func_ref)
{
InputStruct in;
func_ref(in);
}
int main()
{
std::function<bool(InputStruct)> my_func = [](InputStruct & in)->bool{return in.i==1;};
function_rcv(my_func);
}
Checking with godbolt shows this compiles successfully with MSVC, but fails for both Clang and GCC.
Interestingly enough, using a primitive instead of a struct fails compilation on all three compilers.
Is this a bug in the MSVC compiler?
In summary: it is not a compiler bug. MSVC accepts this code because of its default non-conforming behavior, but it can be made standard-conforming with a switch.
First of all, I need to clarify std::function's one aspect: it accepts a function (in general, Callable) which signature is not a perfect match, but the parameters can be converted. Consider:
using intFn = void (int);
void fn(short);
intFn *a = fn; // doesn't compile
std::function<intFn> b = fn; // compiles!
Here, intFn a function type which has an int parameter, while the function fn has a short parameter. The simple function pointer a, cannot be set to point to fn, as the type of the parameter differ (int vs short). But, std::function allows this, so b can be set to point to fn.
In your example, std::function has an InputStruct parameter by value, while the lambda has a non-const lvalue reference InputStruct &. When std::function std::forwards its parameter, it becomes an xvalue, which cannot be bound to the lambda's lvalue reference parameter. That's why standard conforming compilers don't accept this code.
Why does MSVC accept this code? Because it has non-conforming behavior by default: it allows binding class temporaries (and xvalues) to non-const lvalue references. You can disable this behavior with /Zc:referenceBinding (or the older /Za option). If you use this switch, MSVC rejects your example.

sizeof and function template: sizeof(&f) vs sizeof(&f<int>)

As mentioned by [5.3.3/3] (expr.sizeof, working draft):
The sizeof operator can be applied to a pointer to a function, but shall not be applied directly to a function.
The following minimal, working example compiles fine:
void f() { }
int main() {
sizeof(&f);
}
I would expect that also the one below would work:
template<typename T>
void f() { }
int main() {
sizeof(&f<int>);
}
Anyway, even if it compiles with clang (v3.8), it does not using GCC (v6.1).
The error is:
error: address of overloaded function with no contextual type information
I suspect it's a bug of GCC (I will open a ticket if confirmed).
Am I right or I'm missing something here and GCC is right indeed?
Meanwhile, I opened an issue to GCC.
This is a bug. The following code compiles fine:
template<typename T>
void f() { }
int main() {
auto fptr = &f<int>;
return sizeof(fptr);
}
Note that at first I didn't read the question attentively. I was under the impression that the function f<int> is indeed overloaded, for example as below:
template<typename T>
void f() { }
template<typename T>
void f(T) { }
int main() {
sizeof(&f<int>);
}
Under such reading of the question I prepared the following answer, which I still want to share with the community:
I wouldn't qualify it as a bug.
The argument for qualifying it as a bug is the following - all function pointers have the same size, so why does it matter which function is meant inside the sizeof operator?
I am going to defeat that argument.
First of all, it starts from the wrong premise. The C++ standard only guarantees that
converting a prvalue of type “pointer to T1” to the type “pointer to
T2” (where T1 and T2 are function types) and back to its original type yields the original pointer value
which doesn't necessarily mean that the size of pointers to functions of different types is the same. I admit, however, that in practice this is true.
Next, even if we accept the premise and logic behind the argument, then we must also accept the claim that the following program should also compile without any problem:
template<typename T>
void f() { }
template<typename T>
void f(T) { }
int main() {
auto fptr = &f<int>;
return sizeof(fptr);
// fptr is not used anywhere else, so the compiler must not
// whine about the ambiguity on its declaration line
}
Continuing in this manner, we would argue that compilation ambiguities should never be reported provided that they are eliminated by subsequent code.

Why does passing a brace-initialized temporary by address require explicit casting to the same type in MSVS

I was trying to make my code less bloated when dealing with windows API by replacing two-liners not unlike
TEMP t{0,1,2}; // let's say it's struct TEMP {int a; int b; int c}
SomeVeryVerboseFunctionName(&t);
with one-liners
SomeVeryVerboseFunctionName(&TEMP{0,1,2});
but stumbled upon error:
expression must be an lvalue or function designator.
After many attempts I finally came up with code that does compile (MSVS 2013u4):
SomeVeryVerboseFunctionName(&(TEMP) TEMP{0,1,2});//explicit cast to the same type!
To better understand why the cast is needed I set up a simple test project:
#include <stdio.h>
struct A
{
int a;
int b;
A(int _a, int _b) : a(_a), b(_b) {};
};
struct B
{
int a;
int b;
};
template <typename T> void fn(T* in)
{
printf("a = %i, b = %i\n", in->a, in->b);
}
int main()
{
fn(&A{ 1, 2 }); //OK, no extra magick
/* fn(&B {3, 4}); //error: expression must be an lvalue or function designator */
fn(&(B)B{ 3, 4 }); //OK with explicit cast to B (but why?)
}
and found out that if some struct T has an explicit constructor (like A has in the above code), then it's possible to take address of brace-initialized temporary of type T and pass it to a function that takes a pointer T*, but if it doesn't have one (like B), then the said error arises and can only be overcome by explicit casting to type T.
So the question is: why does B require such strange casting and A doesn't ?
Update
Now that it's clear that treating rvalue as lvalue is an extension/feature/bug in MSVS, does anyone care to pretend it's actually a feature (enough used for MS to maintain it since 2010) and elaborate on why temporaries of A and B need to be passed in different ways in order to satisfy the compiler? It must have something to do with A's constructor and B's lack thereof...
What you are doing is actually illegal in C++.
Clang 3.5 complains:
23 : error: taking the address of a temporary object of type 'A' [-Waddress-of-temporary]
fn(&A {1, 2}); //OK, no extra magick
^~~~~~~~~
25 : error: taking the address of a temporary object of type 'B' [-Waddress-of-temporary]
fn(&(B) B {3, 4}); //OK with explicit cast to B (but why?)
^~~~~~~~~~~~~
All operands of & must be lvalues, not temporaries. The fact that MSVC accepts these constructs is a bug. According to the link pointed out by Shafik above, it seems that MSVC erroneously creates lvalues for these.
template<class T>
T& as_lvalue(T&& t){return t;}
// optional, blocks you being able to call as_lvalue on an lvalue:
template<class T>
void as_lvalue(T&)=delete;
will solve your problem using legal C++.
SomeVeryVerboseFunctionName(&as_lvalue(TEMP{0,1,2}));
in a sense, as_lvalue is an inverse-move. You could call it unmove, but that would get confusing.
Taking the address of an rvalue is illegal in C++. The above turns the rvalue into an lvalue, at which point taking the address becomes legal.
The reason why taking an address of an rvalue is illegal is that such data is meant to be discarded. The pointer will only remain valid until the end of the current line (barring the rvalue being created via a cast of an lvalue). Such pointers only have corner-case usefulness. However, in the case of windows APIs, many such APIs take pointers to data structures for C-style versioning purposes.
To that end, these might be safer:
template<class T>
T const& as_lvalue(T&& t){return t;}
template<class T>
T& as_mutable_lvalue(T&& t){return t;}
// optional, blocks you being able to call as_lvalue on an lvalue:
template<class T>
void as_lvalue(T&)=delete;
template<class T>
void as_mutable_lvalue(T&)=delete;
because the more likely to be correct one returns a const reference to data (why are you modifying a temporary?), and the longer one (hence less likely to be used) returns the non-const version.
MSVC has an old "bug"/"feature" where it treats many things as lvalues when it should not, including the result of casts. Use /Za to disable that extension. This can cause otherwise working code to fail to compile. It may even cause working code to fail to work and still compile: I have not proved the contrary.

Inconsistent overload resolution with rvalue references

My understanding of overload resolution is that 'T&&' is generally a better match than 'const T&'. However, I'm seeing some inconsistent behavior among compilers with this trivial example:
#include <iostream>
void Func(const double& a)
{
(void)a;
std::cout << "[lvalue]\n";
}
void Func(double&& a)
{
(void)a;
std::cout << "[rvalue]\n";
}
template <typename T>
void TFunc(T&& a)
{
Func(a);
}
int main ()
{
TFunc(5.5f);
return 0;
}
Clang 3.2 will print [rvalue]. However, the VS2013 32bit/64bit compilers will print [lvalue]. If I change '5.5f' to '5.5', then both compilers will print [lvalue].
I can understand why the VS compiler chooses the 'const double&' version since I don't have a std::forward call to preserve the '&&' of the parameter. However, I still don't understand what makes clang think that the '&&' overload is the better choice.
Why does adding an implicit float to double conversion affect the behavior in clang? Who is right?
When you call with 5.5f, T is float, and the Func call effectively becomes Func(double(a)). The argument is a temporary, and so rvalue overload should be chosen.
When you call with 5.5, T is double, and no temporary is created in a call to Func(a). A named variable cannot bind to an rvalue reference, so lvalue overload is the only choice.
MSVC has a long-standing misfeature that allows temporaries to bind to non-const lvalue references. This is why it incorrectly chooses lvalue overload even in the first case. Try compiling with /Za (disable language extensions) - I believe it would match clang's behavior then.

Use of std::declval on fundamental types

The following code fails to compile when the template parameter T is a fundamental type such as int (on gcc 4.8). Is this standard conforming behaviour? My understanding of std::declval was that it always resolves to either a T&& or T&.
template <class T>
void foo(T&& val)
{
std::cout << noexcept(std::declval<typename std::decay<T>::type>() = val);
}
struct bar { };
bar b;
foo(b); // okay
int a;
foo(a); // error: using xvalue (rvalue reference) as lvalue
The error occurs at the point of assigning val to the std::declval expression.
It works if I remove the std::decay and use std::declval<T> directly, but I'm not sure why. The decayed type should just be int and so std::declval<int>() should have a return type of int&& shouldn't it?
You're trying to assign to an rvalue. std::declval<int>() correctly returns a type int&&. It has no name, so it's an rvalue (more precisely, xvalue) of type int. You're then trying to assign val into this rvalue, which is illegal for fundamental types.
Here's a live example demonstrating the problem in simplified form (without declval).