I was interested in learning more about the behaviour of std::bind() with member functions, and thought it was odd for MSVC to compile the following code when no other compiler I tried would. Is MSVC wrong to compile this?
I tried GCC, CLang, and some other minor compilers in the compiler explorer side-by-side with MSVC: https://godbolt.org/z/DNtP-o
Only MSVC would compile this:
#include <functional>
struct S{
void f(int){}
};
int main(){
S s;
auto binding = std::bind(&S::f,s, 5,3);
return 0;
}
These are the errors I get from compiling with 'x86-64 clang 8.0.0':
error: static_assert failed due to requirement 'integral_constant<bool, false>::value ? sizeof...(_BoundArgs) >= integral_constant<unsigned long, 1>::value + 1 : sizeof...(_BoundArgs) == integral_constant<unsigned long, 1>::value + 1' "Wrong number of arguments for pointer-to-member"
error: no matching function for call to 'bind'
None of the compilers are wrong here, since the program has undefined behavior.
Note that MSVC does of course give errors if you actually try to call the bound functor. So the question is fairly academic, since in a real program using std::bind to create something that can never be called on any code path is not very useful.
C++17 [func.bind.bind]/2 says:
Requires: ... INVOKE(fd, w_1, w_2, ..., w_N) shall be a valid expression for some values w_1, w_2, ... w_N, where N has the value sizeof...(bound_args). ...
But this is a requirement on code using the standard library, not on implementations (compilers and/or libraries), and nothing says implementations must diagnose a violation. Violating a Requires: paragraph is undefined behavior unless otherwise noted.
So the ability of g++ and clang++ (when using libstdc++) to notice the problem at the std::bind call is just a nice quality of implementation bonus feature.
Presumably libstdc++ does this by template matching on a pointer to (member) function. But in the more general case, determining whether there might be any way to call a functor with a given number of arguments of wildcard types is more difficult or impossible. All three compilers accept this code with no errors or warnings:
#include <functional>
struct X {
void operator()() const;
void operator()(int) const;
};
void f() {
auto func = std::bind(X{}, 2, 3, 4);
static_cast<void>(func);
}
Related
I've been experimenting with different forms of UB in order to better understand the current compiler inadequacies using constexpr evaluations to detect UB.
Note: There is normally no compiler requirement to emit diagnostics when UB occurs. However, this is not the case when executing compile time code such as consteval functions as shown here. Even there, there is an exception for library code where compilers are not required to emit diagnostics for UB. The question here does not use library code and compilers should emit diagnostics when encountering UB executing code at compile time.
I ran across an odd situation where modifying the underlying memory of an const int in automatic storage (aka stack) results in no warnings with CLANG, GCC, and MSVC but when evaluated in a constexpr function CLANG and GCC elicit the expected error while MSVC gives no error.
The function being evaluated casts a const int to a non const through it's address to attempt to change it's value from 1 to 2. Afterwards, the const value is added to that returned from a function called with a const int& that simply returns the argument value.
This is all UB, of course. However, all three compilers happily compile and run when not in a constexpr function. While UB, they are not required to emit diagnostics. And they interpret the result as the original const value when the variable is used directly but as the modified value when accessed through a reference.
But when running in compile time code, GCC and CLANG identify the UB as, for example:
modification of object of const-qualified type 'const int' is not allowed in a constant expression
MSVC produces no error or warning.
Since this uses no library code in the constexpr evaluation, I believe MSVC is in error not reporting the UB. Is this correct?
Compiler Explorer
#include <iostream>
// un-comment out the next line to evaluate at runtime
// #define consteval
consteval int ret_arg(const int& v) { return v; }
consteval int f()
{
const int i{ 1 };
*const_cast<int*>(&i) = 2;
return i + ret_arg(i);
}
int main()
{
std::cout << f() << "\n";
}
Run time UB w/o compiler errors for all compilers can be seen by commenting the "#define consteval" line at the top.
I was interested in learning more about the behaviour of std::bind() with member functions, and thought it was odd for MSVC to compile the following code when no other compiler I tried would. Is MSVC wrong to compile this?
I tried GCC, CLang, and some other minor compilers in the compiler explorer side-by-side with MSVC: https://godbolt.org/z/DNtP-o
Only MSVC would compile this:
#include <functional>
struct S{
void f(int){}
};
int main(){
S s;
auto binding = std::bind(&S::f,s, 5,3);
return 0;
}
These are the errors I get from compiling with 'x86-64 clang 8.0.0':
error: static_assert failed due to requirement 'integral_constant<bool, false>::value ? sizeof...(_BoundArgs) >= integral_constant<unsigned long, 1>::value + 1 : sizeof...(_BoundArgs) == integral_constant<unsigned long, 1>::value + 1' "Wrong number of arguments for pointer-to-member"
error: no matching function for call to 'bind'
None of the compilers are wrong here, since the program has undefined behavior.
Note that MSVC does of course give errors if you actually try to call the bound functor. So the question is fairly academic, since in a real program using std::bind to create something that can never be called on any code path is not very useful.
C++17 [func.bind.bind]/2 says:
Requires: ... INVOKE(fd, w_1, w_2, ..., w_N) shall be a valid expression for some values w_1, w_2, ... w_N, where N has the value sizeof...(bound_args). ...
But this is a requirement on code using the standard library, not on implementations (compilers and/or libraries), and nothing says implementations must diagnose a violation. Violating a Requires: paragraph is undefined behavior unless otherwise noted.
So the ability of g++ and clang++ (when using libstdc++) to notice the problem at the std::bind call is just a nice quality of implementation bonus feature.
Presumably libstdc++ does this by template matching on a pointer to (member) function. But in the more general case, determining whether there might be any way to call a functor with a given number of arguments of wildcard types is more difficult or impossible. All three compilers accept this code with no errors or warnings:
#include <functional>
struct X {
void operator()() const;
void operator()(int) const;
};
void f() {
auto func = std::bind(X{}, 2, 3, 4);
static_cast<void>(func);
}
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.
I am curious why there is a difference in the argument evaluation order between chained static functions and member functions. From the answers at this question I can see it is unspecified what the argument evaluation order is between such chained function calls. Take for example the following snippet:
#include <iostream>
class test {
public:
static test& chain_s(test& t, int i) {
std::cout << i << " ";
return t;
}
test& chain(test& t, int i) {
std::cout << i << " ";
return *this;
}
};
int main(int, char**) {
int x = 2;
test t;
t.chain(t,++x).chain(t,++x).chain(t,++x);
x = 2; std::cout << std::endl;
t.chain_s(t,++x).chain_s(t,++x).chain_s(t,++x);
return 0;
}
In the case of GCC 4.6.2 and CL 15.00.30729.01 (MSVC 9) the resulting output is for me
5 5 5
3 4 5
However, I was wondering if there is any reason in the specification or if it is otherwise known why the static function are evaluated left-to-right (with their arguments), and for the non-static function all the arguments first (right-to-left from what I've seen in other tests).
The reason I'm asking this is because I first noticed this difference in behavior when trying to get similar behavior in C (using a struct and a function pointer) and failed. I strongly suspect this is some optimization implemented both in GCC and MSVC for member functions, but I hope someone here can shed a little more light on this.
Edit:
I forgot to mention one crucial bit of information which strikes me as odd: GCC will only warn on unspecified behavior on the chained non-static function, but not the static functions:
a.cpp: In function 'int main(int, char**)':
a.cpp:18:45: warning: operation on 'x' may be undefined [-Wsequence-point]
GCC is not obligated to provide such warnings so it could miss the second expression, but this is what leads me to believe something interesting is going on.
No reason. Like you say, the order is unspecified by the language.
One reason for using right to left order is that functions with a variable number of parameters, like printf, will then always have the first parameter on top. Otherwise it doesn't matter.
Your code has undefined behavior, but I suppose you know that. Also,
you could easily see a difference depending on optimization flags. But
in this case, one likely reason is that the non-static functions require
three arguments, including the results of the previous call, where as
the static functions only require two, and the results of the previous
call are ignored.
I have an issue in which Clang (3.6) and G++ (5.1) have a differing opinion:
#include <functional>
struct X
{
X()
{
std::function<void (int)> f = [this](auto x){foo(x);};
}
void foo(int x){}
};
int main(){}
Clang accepts this, whereas G++ states:
error: cannot call member function ‘void X::foo(int)’ without object
Both compilers accept it if I call this->foo(x) directly instead, but I'd rather know who's right.
Note: both the "auto" in the lambda signature and the conversion to a std::function<> are required to trigger this case.
Both compilers accept it if I call this->foo(x) directly instead, but I'd rather know who's right.
Considering it compiles in gcc 5.2, clang is the one correct in your specific case. It looks like it was just a bug in gcc 5.1. gcc 6.0 also compiles this fine.
Plus it makes intuitive sense, this should be implied.