lambdas and equality/inequality operator - c++

I have a question about equality comparison of lambdas.
I've tried to read some references but I've found nothing about this.
[] (Args ...args) -> ReturnType { ... };
For this type of lambdas, which are not closures actually because they have empty capture list, operators == and != works in the same way as for static functions (well, it seems, compiler generates them as static functions as well). But for closures any attempt to compare in the same way causes compilation error.
Here is simple programm for example:
#include <typeinfo>
#include <iostream>
struct WrapperBase {
virtual ~WrapperBase() = default;
virtual bool operator==(WrapperBase& v) = 0;
virtual bool operator!=(WrapperBase& v) = 0;
};
template<typename _Tp>
struct Wrapper : WrapperBase {
Wrapper(const _Tp& v) : value(v) { }
bool operator==(WrapperBase& v) override {
try {
Wrapper<_Tp>& vv = dynamic_cast<Wrapper<_Tp>&>(v);
return value == vv.value;
}
catch(std::bad_cast& err) { }
return false;
}
bool operator!=(WrapperBase& v) override {
try {
Wrapper<_Tp>& vv = dynamic_cast<Wrapper<_Tp>&>(v);
return value != vv.value;
}
catch(std::bad_cast& err) { }
return true;
}
//
_Tp value;
};
template<typename _Tp>
WrapperBase* create_wrapper(const _Tp& v) {
return new Wrapper<_Tp>(v);
}
struct Base {
Base(int a, int b) : wrapper(nullptr), a(a), b(b) { }
virtual ~Base() { delete wrapper; }
virtual WrapperBase* create_wrapper() = 0;
WrapperBase* wrapper;
int a;
int b;
};
struct ClassA : Base {
ClassA(int a, int b) : Base(a, b) {
wrapper = create_wrapper();
}
WrapperBase* create_wrapper() override {
auto lambda = [] (int v1, int v2) { return v1 + v2; };
return ::create_wrapper(lambda);
}
};
struct ClassB : Base {
ClassB(int a, int b) : Base(a, b) {
wrapper = create_wrapper();
}
WrapperBase* create_wrapper() override {
auto lambda = [=] (int v1, int v2) { return a + b + v1 + v2; };
return ::create_wrapper(lambda);
}
};
int main(int argc, char** argv) {
std::cout << std::boolalpha;
// all works fine:
ClassA a1(1, 2);
ClassA a2(3, 4);
std::cout << (*a1.wrapper == *a1.wrapper) << std::endl; // true
std::cout << (*a2.wrapper == *a2.wrapper) << std::endl; // true
std::cout << (*a1.wrapper == *a2.wrapper) << std::endl; // true
// cause compilation error:
ClassB b1(1, 2);
ClassB b2(3, 4);
std::cout << (*b1.wrapper == *b1.wrapper) << std::endl;
std::cout << (*b2.wrapper == *b2.wrapper) << std::endl;
std::cout << (*b1.wrapper == *b2.wrapper) << std::endl;
return 0;
}
Comparing lambdas created in instances of ClassA always return true even if they are created in different context (just as I said). On the other hand, ClassB do not even compile because operator == and != for its lambda is not found.
It seems, this programm is not well-formed, and comparing of lambdas in the way I've tried causes undefined behavior of the programm. But if it's really undefined behavior, how can they be compared? (I guess, nohow)

For this type of lambdas, which are not closures actually because they
have empty capture list, operators == and != works in the same way as
for static functions (well, it seems, compiler generates them as
static functions as well).
It works because the closure type of a lambda without captures provides a conversion operator that returns a function pointer. Those are comparable. [expr.prim.lambda]/6 (emphasis mine):
The closure type for a lambda-expression with no lambda-capture
has a public non-virtual non-explicit const conversion function to
pointer to function having the same parameter and return types as the
closure type’s function call operator. The value returned by this
conversion function shall be the address of a function that, when
invoked, has the same effect as invoking the closure type’s function
call operator.
(If the conversion operator was explicit the comparison would not work)
Roughly, a lambda of the form [] {} translates to
struct closure_type
{
private:
static void call() {}
public:
// closure_type() = delete; // Commented for the sake of the demo
closure_type& operator=(closure_type const&) = delete;
void operator()() const { /*return call();*/ }
operator decltype(&call)() const
{
return &call;
}
};
As you may have noted, the conversion operator returns the same function pointer each time. Though it would be utterly surprising if anything the like happened, the standard does allow different function pointers to be returned for a call to the conversion operator for the same closure object; A comparison of two closure objects has therefore an implementation-defined value. (It should, though, on all implementations be true for two closure objects of the same type.)

Related

Concept-restricted range-based for loop of std::list<std::reference_wrapper<T>>

I have some class Foo and a std::list<std::reference_wrapper<Foo>> and would like to iterate over its elements with a range-based for loop:
#include <list>
#include <functional>
#include <iostream>
class Foo {
public:
Foo(int a) : a(a) {}
int a;
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(auto &foo : refs) {
std::cout << foo.get().a << std::endl;
}
for(Foo &foo : refs) {
std::cout << foo.a << std::endl;
}
return 0;
}
Notice the additional get() when catching with auto, as we deduce type std::reference_wrapper<Foo>, whereas in the second case foo is already implicitly converted to type Foo& as we explicitly catch with this type.
I was actually looking for a way to catch with auto but implicitly cast away the std::reference_wrapper implicitly in order to not have to bother with the get() method all the time in the for body, so I tried introducing a fitting concept and catching with this, i.e. I tried
//this is not legal code
template<typename T>
concept LikeFoo = requires (T t) {
{ t.a };
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(LikeFoo auto &foo : refs) {
std::cout << foo.a << std::endl;
}
return 0;
}
and hoped that it would work. clang however deduces the type of foo to std::reference_wrapper<Foo>, so that in fact below code will be correct:
//this compiles with clang, but not with gcc
template<typename T>
concept LikeFoo = requires (T t) {
{ t.a };
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(LikeFoo auto &foo : refs) {
std::cout << foo.get().a << std::endl;
}
return 0;
}
However, gcc completely refuses to accept the range-based for loop and complains deduced initializer does not satisfy placeholder constraints, as it tries to check LikeFoo<std::reference_wrapper<Foo>>, which of course evaluates to false, so with gcc one cannot even catch foo concept-restricted. Two questions arise:
Which of the compilers is correct? Should LikeFoo auto& foo : refs be valid?
Is there a way to auto-catch (possibly concept-restricted) foo : refs such that one can avoid having to write get() in the for-loop body?
You can find this example at the Compiler explorer.
Which of the compilers is correct? Should LikeFoo auto& foo : refs be valid?
No. refs is a range of reference_wrapper<Foo>&, so foo deduces to a reference to reference_wrapper<Foo> - which does not have a member named a. A constrained variable declaration doesn't change how deduction works, it just effectively behaves like an extra static_assert.
Is there a way to auto-catch (possibly concept-restricted) foo : refs such that one can avoid having to write get() in the for-loop body?
Just by writing refs? No. But you can write a range adaptor to convert your range of reference_wrapper<T> to a range of T&. There is already such a thing in the standard library, transform:
for (auto &foo : refs | std::views::transform([](auto r) -> decltype(auto) { return r.get(); })) {
That's a mouthful, so we can make it its own named adaptor:
inline constexpr auto unwrap_ref = std::views::transform(
[]<typename T>(std::reference_wrapper<T> ref) -> T& { return ref; });
And then you can write either:
for (auto &foo : refs | unwrap_ref) { ... }
for (auto &foo : unwrap_ref(refs)) { ... }
Either way, foo here deduces to be a Foo.
With a little bit more work, you can write a range adaptor that unwraps reference_wrapper<T> but preserves any other reference type.
Here is a bare minimum working example for a wrapper that calls get when being dereferenced.
#include <list>
#include <functional>
#include <iostream>
template <typename T>
struct reference_wrapper_unpacker {
struct iterator {
typename T::iterator it;
iterator& operator++() {
it++;
return *this;
}
iterator& operator--() {
it--;
return *this;
}
typename T::value_type::type& operator*() {
return it->get();
}
bool operator!=(const iterator& other) const {
return it != other.it;
}
};
reference_wrapper_unpacker(T& container) : t(container) {}
T& t;
iterator begin() const {
return {t.begin()};
}
iterator end() const {
return {t.end()};
}
};
class Foo {
public:
Foo(int a) : a(a) {}
int a;
};
int main() {
std::list<Foo> ls = {{1},{2},{3},{4}};
std::list<std::reference_wrapper<Foo>> refs(ls.begin(), std::next(ls.begin(),2));
for(auto &foo : refs) {
std::cout << foo.get().a << std::endl;
}
for(Foo &foo : refs) {
std::cout << foo.a << std::endl;
}
for(auto &foo : reference_wrapper_unpacker{refs}) {
std::cout << foo.a << std::endl;
}
return 0;
}
To make it usable in generic code you would need to SFINAE to detect if the container actually has a reference_wrapper, and if not, just return the original container.
I'll leave that part out since it was not a part of the original question.

Innocent range based for loop not working

The following does not compile:
#include <iostream>
int main()
{
int a{},b{},c{},d{};
for (auto& s : {a, b, c, d}) {
s = 1;
}
std::cout << a << std::endl;
return 0;
}
Try it on godbolt
Compiler error is: error: assignment of read-only reference 's'
Now in my actual case the list is made of member variables on a class.
Now, this doesn't work because the expression becomes an initializer_list<int> that actually copies a,b,c, and d - hence also not allowing modification.
My question is two-fold:
Is there any motivation behind not allowing to write a range-based for loop in this way ? eg. perhaps there could be a special case for naked brace expressions.
What is a syntactical neat way of fixing this type of loop ?
Something along this line would be preferred:
for (auto& s : something(a, b, c, d)) {
s = 1;
}
I do not consider pointer indirection a good solution (that is {&a, &b, &c, &d}) - any solution should give the element reference directly when the iterator is de-referenced.
Ranges are not as magic as people would like. In the end, there must be an object that the compiler can generate calls on to either a member function or free function begin() and end().
Closest you'll probably be able to come is:
#include <iostream>
int main()
{
int a{},b{},c{},d{};
for (auto s : {&a, &b, &c, &d} ) {
*s = 1;
}
std::cout << a << "\n";
return 0;
}
Just another solution within a wrapper idea:
template<typename T, std::size_t size>
class Ref_array {
using Array = std::array<T*, size>;
class Iterator {
public:
explicit Iterator(typename Array::iterator it) : it_(it) {}
void operator++() { ++it_; }
bool operator!=(const Iterator& other) const { return it_ != other.it_; }
decltype(auto) operator*() const { return **it_; }
private:
typename Array::iterator it_;
};
public:
explicit Ref_array(Array args) : args_(args) {}
auto begin() { return Iterator(args_.begin()); }
auto end() { return Iterator(args_.end()); }
private:
Array args_;
};
template<typename T, typename... Ts>
auto something(T& first, Ts&... rest) {
static_assert((std::is_same_v<T, Ts> && ...));
return Ref_array<T, 1 + sizeof...(Ts)>({&first, &rest...});
}
Then:
int main() {
int a{}, b{}, c{}, d{};
for (auto& s : something(a, b, c, d)) {
std::cout << s;
s = 1;
}
std::cout << std::endl;
for (auto& s : something(a, b, c, d))
std::cout << s;
}
outputs
0000
1111
According to the standard §11.6.4 List-initialization/p5 [dcl.init.list] [Emphasis Mine]:
An object of type 'std::initializer_list' is constructed from an
initializer list as if the implementation generated and materialized
(7.4) a prvalue of type “array of N const E”, where N is the number of
elements in the initializer list. Each element of that array is
copy-initialized with the corresponding element of the initializer
list, and the std::initializer_list object is constructed to refer
to that array. [ Note: A constructor or conversion function selected
for the copy shall be accessible (Clause 14) in the context of the
initializer list. — end note ] If a narrowing conversion is required
to initialize any of the elements, the program is ill-formed.
Thus, your compiler is complaining legitimately (i.e., auto &s deducts to int const& s and you cannot assign to s in the ranged for loop).
You could alleviate this problem by introducing a container instead of an initializer list (e.g., `std::vector’) with ‘std::reference_wrapper’:
#include <iostream>
#include <vector>
#include <functional>
int main()
{
int a{},b{},c{},d{};
for (auto& s : std::vector<std::reference_wrapper<int>>{a, b, c, d}) {
s.get()= 1;
}
std::cout << a << std::endl;
return 0;
}
Live Demo
To satisfy that syntax
for (auto& s : something{a, b, c, d}) {
s = 1;
}
you might create wrapper:
template <typename T>
struct MyRefWrapper
{
public:
MyRefWrapper(T& p) : p(&p) {}
T& operator =(const T& value) const { return *p = value; }
operator T& () const { return *p; }
private:
T* p;
};
Demo
Solution: use a reference wrapper
template <class It>
struct range_view_iterator : public It{//TODO: don't inherit It
auto& operator*() {
return (*this)->get();
}
};
template<class It>
range_view_iterator(It) -> range_view_iterator<It>;
template<class T>
struct range_view {
std::vector<std::reference_wrapper<T> > refs_;
range_view(std::initializer_list<std::reference_wrapper<T> > refs) : refs_{refs} {
}
auto begin() {
return range_view_iterator{ refs_.begin() };
}
auto end() {
return range_view_iterator{ refs_.end() };
}
};
Then used as:
for (auto& e : range_view<int>{a, b, c, d}) {
e = 1;
}
This doesn't try to answer the first question though.
You could create wrapper class for storing reference and which will have assignment operator to update this value:
template<class T>
struct Wrapper {
T& ref;
Wrapper(T& ref)
: ref(ref){}
template<class U>
void operator=(U u) {
ref = u;
}
};
template<class...T>
auto sth(T&...t) {
return std::array< Wrapper<std::common_type_t<T...> > ,sizeof...(t) >{Wrapper(t)...};
};
int main(){
int a{},b{},c{},d{};
for (auto s : sth(a,b,c,d)) {
s = 1;
}
std::cout << a << std::endl; // 1
Live demo

C++ User-defined type conversion failure

template<typename T>
class Pack
{
private:
std::function<T()> _Func = nullptr;
public:
Pack()
{
}
Pack(std::function<T()> func)
: _Func(func)
{
}
~Pack()
{
}
operator T()
{
return _Func();
}
};
What I use is operator T, I want to call _Func implicitly but I cannot even do it explicitly. It seems right but actually error C2440 #MSVC. I use it in two ways:
static member of class (succeeded);
member of class (failed)
(I don't know whether it matters or not)
I'm really wondering why it performs in two ways, and more importantly, how I can put it into my class as a non-static member and successfully call the operator T.
Member of the class:
struct test
{
test()
{
p_ = Pack<int>(std::bind(&test::foo, *this));
}
int foo()
{
std::cout << "test::foo" << std::endl;
return 5;
}
Pack<int> p_;
};
int main()
{
test t;
int x = t.p_;
return 0;
}
This works fine on VS 2013 EE.

g++ - how do I disable implicit conversion from 0 to pointer types?

Specifically, I want the following code to fail:
void a(void*){}
int main(){
a(0); // FAIL
a(NULL); // FAIL
a(nullptr); // success
}
And I want the following code to compile:
void a(int){}
void a(void*){}
int main(){
a(0); // calls first a
a(NULL); // calls first a; that's why I have -Werror
a(nullptr); // calls second a
}
The following code does not compile currently, but should according to my rule:
void a(std::size_t){}
void a(void*){}
int main(){
a(0); // two candidates
}
Any idea how to make g++ behave like that?
You can compile with -Wzero-as-null-pointer-constant to get a warning when you use 0 or NULL instead of nullptr. To promote that to an error, I believe using -Werror=zero-as-null-pointer-constant would work.
Unfortunately, this is simply a warning and is not able to change overload resolution rules. I also believe NULL must be defined as 0 rather than nullptr in order for the warning to catch it, but at least as of GCC 4.9, std::is_null_pointer<decltype(NULL)>::value is false and GCC warns when using NULL.
This may not be perfect, but if you trully want to have overloads with int and pointer, you could use some helper class like this:
#include <iostream>
#include <iomanip>
using std::cout;
using std::endl;
template<typename T = void> class ptr {
T* it;
public:
ptr(T* it = nullptr): it(it) {}
ptr(const ptr<T>&) = default;
ptr& operator = (const ptr<T>&) = default;
operator T* () { return it; }
T& operator * () { return *it; }
T* operator -> () { return it; }
ptr& operator += (int x) { it += x; return *this; }
ptr& operator -= (int x) { it -= x; return *this; }
ptr& operator ++ () { ++it; return *this; }
// etc...
public:
template<typename P>
ptr(P* it): it(it) {}
template<typename P>
ptr(ptr<P> it): it((T*)it) {}
};
template<> class ptr<void> {
void* it;
public:
ptr(void* it = nullptr): it(it) {}
ptr(const ptr<void>&) = default;
ptr& operator = (const ptr<void>&) = default;
operator void* () { return it; }
public:
template<typename P>
ptr(P* it): it(it) {}
template<typename P>
ptr(ptr<P> it): it((void*)it) {}
};
void a(std::size_t x) {
cout << "first: " << x << endl; }
void a(ptr<const int> p) {
cout << "second: " << (p ? *p : -1) << endl; }
void a(ptr<int> p, ptr<> q) {
cout << "third: " << (p ? *p : -1) << ", "
<< (q ? "some" : "null") << endl;
a(p); }
int main(){
a(0); // first: 0
a(NULL); // first: 0 but warning [-Wconversion-null]
a(new int(3), nullptr); // third: 3, null + second: 3
}
It is not finished (maybe remove that explicit, add more operators, special conversion from nullptr_t, etc), just and idea.
EDIT: Few changes in code, template constructors and conversion to ptr<const int> test.
Given that NULL is either identical to 0 or nullptr, I don't think you can force a C++ compiler to behave the way you describe it. I could imagine using clang's AST interface to detect the cases exactly the way you describe. I'd expect that typical C++ code will contain a number of intentional uses of 0 and/or NULL to mean pointers and/or integers as appropriate.
Here is a relatively simple solution to the first problem (it requires C++11):
struct must_be_void_ptr{
must_be_void_ptr(void* p) : p(p) {}
must_be_void_ptr(int) = delete; // Disallow implicit conversion from 0
void* p;
operator void*() { return p; }
};
void a(must_be_void_ptr p){
void* vp = p;
}
int main(){
a(nullptr);
a(0);
}
Use:
gsl::not_null
From Guideline Support Library. I highly recommend GSL. It's created and backed by many C++ experts, Bjarne Stroustrup himself and Herb Sutter among them. And the C++ Core Guidelines are actively being integrated into the compiler warnings and static analyzers.

Overloaded 'dereference' or 'member of pointer' operators don't get run when I have a pointer to an object

I have the following code:
#include <iostream>
struct Base {
int i_;
};
class El : protected Base {
public:
int get_i() const { return i_; }
void set_i(int i) { i_ = i; }
};
class It : protected Base {
public:
using pointer = const El*;
using reference = const El&;
reference operator*() const
{
return reinterpret_cast<reference>(*this);
}
pointer operator->() const
{
return reinterpret_cast<pointer>(this);
}
};
int main()
{
It it;
It* itp = &it;
std::cout << *****(itp)->get_i() << "\n"; //ERROR
}
Both GCC and Clang++ somehow fail to invoke either of operator* or operator->, so I get an error It doesn't have member function 'get_i' in the last line regardless how many indirections I try. Does the standard warrant such unintuitive behavior?
Operator precedence: -> binds more tightly, so is applied to the pointer itp.
When you overload operator->, that doesn't affect the meaning of operator-> applied to a pointer-to-your-class. You want (*itp)->get_i();, I think.