boost::tuple with member function pointer - c++

For some reason I get this error message
invalid operands of types 'void (S::* const)()' and 'void (S::* const)()' to binary 'operator<'
for this code snippet:
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
struct S
{
void f() {}
};
typedef void(S::*tdef)();
int main()
{
boost::tuple<tdef> t1(&S::f);
boost::tuple<tdef> t2(&S::f);
return t1 < t2;
}
Boost documents are very tight-lipped on using member function pointers in tuples (apart from they are valid elements), so I don't really have a clue what could be the problem or how those 'const' qualifiers got into the expression.
Any hint?

Tuples will try to do the comparison on a function pointer and you can only compare function pointers for equality. Please also refer to this question
Function pointers are not relationally comparable in C++. Equality comparisons are supported, except for situations when at least one of the pointers actually points to a virtual member function (in which case the result is unspecified).

Related

Data member pointers as associative container keys

I am trying to create an std::set of pointers to data members. However, I can't find a method to sort or hash such pointers.
They can't be compared with operator<, they don't seem to be supported by std::less and there is no standard integer type that is guaranteed to hold their representation (they might not fit in std::uintptr_t).
This is what I tried first (https://godbolt.org/z/K8ajn3rM8) :
#include <set>
struct foo
{
int x;
int y;
};
using t_member_ptr = int (foo::*);
const std::set<t_member_ptr> members = {
&foo::x,
&foo::y
};
It produces the error error: invalid operands of types 'int foo::* const' and 'int foo::* const' to binary 'operator<'. The full error message also implies this occurs during instantiation of std::less.
I found a similar question (Set of pointer to member) but it dates back to C++14 and the answers boil down to "put your pointers in a vector and perform a linear search instead".
Has there been any changes with C++17 or C++20 that make it possible to use pointers to data members as keys for standard associative containers?
Compare them bytewise, e.g. using this comparator:
#include <cstring>
#include <type_traits>
struct BitLess
{
template <typename T>
requires std::has_unique_object_representations_v<T>
constexpr bool operator()(const T &a, const T &b) const
{
return std::memcmp(reinterpret_cast<const char *>(&a), reinterpret_cast<const char *>(&b), sizeof(T)) < 0;
}
};
Checking std::has_unique_object_representations_v<T> ensures that there's no padding inside. It tried it on GCC, Clang, and MSVC, and it returned true for member pointers on all three.

vector<bool> raises an error on const data() method

I have the following code:
#include <vector>
struct TestStruct {
std::vector<float> float_vect;
std::vector<bool> bool_vect;
};
void func(const TestStruct & test)
{
const float * p1 = test.float_vect.data(); //<--- this line works fine
const bool * p2 = test.bool_vect.data(); //<--- on this line error happens
}
int main()
{
TestStruct test;
func(test);
}
Error message:
passing 'const std::vector' as 'this' argument of 'void std::vector::data() [with _Alloc = std::allocator]' discards qualifiers [-fpermissive]
data() method of std::vector have const specified.
Why this method works fine on float vector, and raises an error on boolean vector ?
vector<bool> is a specialization of a good old vector<T> and it may be implemented differently from ordinary vector (e.g. some space-saving optimizations may be employed). Side-effect of such design is that it does not always behave as ordinary vector (many consider vector<bool> to be broken because of that).
For example, the reference at http://en.cppreference.com/w/cpp/container/vector_bool does not mention vector<bool>::data() at all. Therefore - you should not use it when using vector with type bool. The fact that you don't get an error similar to method not found is - in your case - just a matter of how vector<bool> is implemented by your compiler.
std::vector<bool> is a template specialization of the class std::vector<T> in the STL. In order to use less memory, the boolean values are stored by 8 in a byte. It means there is no direct data accessible as you expect because the class doesn't store it in the same way than for other types.
Look at the doc :
The specialization has the same member functions as the unspecialized vector, except data, emplace, and emplace_back, that are not present in this specialization.

Pointer to deque<int>::push_back

#include <iostream>
#include <deque>
using namespace std;
main()
{
typedef void (deque<int>::*func_ptr)(int);
func_ptr fptr = &deque<int>::push_back;
}
Im trying to get pointer to this function but I get a compilation error
error: cannot convert ‘void (std::deque<int>::*)(const value_type&) {aka void (std::deque<int>::*)(const int&)}’ to ‘func_ptr {aka void (std::deque<int>::*)(int)}’ in initialization
func_ptr fptr = &deque<int>::push_back;
I want to do this so that I can get pointer to different member functions on the basis of different conditions.
I referred this link.
As said in the accepted answer, the problem is that the type signature is different -- for a std::deque<T>, push_back only has an overload that accepts T const&, and not T directly. typedef void (deque<int>::*func_ptr)(const int &) is a perfectly concise and cromulent way to write this.
I wanted to address a C++11 way to do this -- type aliases. One may first wonder "why not use auto?" This is not possible because the function being accessed is an overloaded function, and auto does not know which of the overloads to access. Because the original question embeds the knowledge of the function type being void(int), the statement &deque<int>::push_back selects the correct overload. The problem with the simpler statement is that it bakes in knowledge of the container and the type being contained. If you want to change to a std::vector, or from int to short, you'll have to make all new types. With type aliases, we can templatize everything to avoid embedding type knowledge:
using func_ptr = void(Cont::*)(typename Cont::const_reference);
We can do something simple, as before:
func_ptr<deque<int>> fptr = &deque<int>::push_back;
...or we can refer to a container somewhere:
vector<short> container;
...looking up its type at compile time, and storing a pointer to its push_back function, all without caring what the container is:
using container_type = decltype(container);
func_ptr<container_type> fptr2 = &container_type::push_back;
push_back() takes a const T & parameter, as the error message states:
cannot convert ‘void (std::deque::*)(const value_type&) ...
Change your type alias to:
typedef void (deque<int>::*func_ptr)(const int &);
There is a bigger problem with what you are trying to do. Implementations are allowed to add default parameters to any member functions, so the actual signature for push_back() might be different. It can even add additional functions with the same name (but a different signature).
I advise against taking pointers to container members function. You are usually better of without it.

Stack Template Arguments

Focus on the template arguments
I can create a stack (an adapter class template from standard library) object like this,
stack<int, vector<int>> myStack;
I know the second template argument means the underlying data structure of the stack. But why the following line doesn't give a compile time error?
stack<int, vector<string>> myStack;
Notice that I'm declaring a stack to contain elements of type int, but at the same time I'm declaring the underlying data structure to hold string elements.
Functionally, It works as if it was a stack of string elements.
What you are doing is undefined behaviour. Nevertheless, I am going to explain why it seems to work fine.
The container adapter std::stack<T, TContainer> contains several type symbols that are aliases for types that will be commonly used. There is a list here.
One that concerns us here is std::stack::value_type. It basically determines what type the methods std::stack::push and friends expect:
void push( const value_type& value );
We can also see how it is defined:
using value_type = typename TContainer::value_type
So, the type that is used in all actions is actually only based on the second type, TContainer ! In your case, that is vector<string>::value_type, so value_type will be an alias to string. The type used for T, int in your case, is not used.
Thus, everything seems to work.
But even though this works in your case with your particular compiler, it is actually not allowed:
The behavior is undefined if T is not the same type as Container::value_type. (since C++17)
You can find the source for this quote here.
From the documentation:
T - The type of the stored elements. The behavior is undefined if T is not the same type as Container::value_type. (since C++17)
Undefined behaviour means it might compile, it might even work, or it might wipe your hard drive.
How it fails, if it does, is implementation dependant.
If I had to guess, I would imagine that your implementation is discarding the int template argument, and just uses Container::value_type instead.
Once we get to C++17, this will be explicitly illegal. Some compilers will already not appreciate this code..
For example:
#include <stack>
#include <string>
#include <vector>
int main() {
std::stack<int, std::vector<std::string>> s;
s.push(3);
}
Fails to compile under OS X (clang, libc++, c++11) with:
blah.cc:7:7: error: no matching member function for call to 'push'
s.push(3);
~~^~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/stack:194:10: note:
candidate function not viable: no known conversion from 'int' to 'const value_type' (aka
'const std::__1::basic_string<char>') for 1st argument
void push(const value_type& __v) {c.push_back(__v);}
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/stack:197:10: note:
candidate function not viable: no known conversion from 'int' to 'value_type' (aka 'std::__1::basic_string<char>') for
1st argument
void push(value_type&& __v) {c.push_back(_VSTD::move(__v));}
^
1 error generated.

How does template converting operator to pointer to member function syntax work

I was looking at the emulated version of nullptr and saw this converting operator (a member of nullptr_t):
template<class C, class T> // or any type of null
operator T C::*() const // member pointer...
{ return 0; }
This syntax for pointer to member function confuses me. I usually expect to see such a type as something like
R (C::*)(I1, I2, ...)
With the template above, there's no input arguments. I can't figure out how the type deduction works in this case. I'm having trouble forming a specific question, other than, how does this work? If I have code like this:
typedef int (MyClass::*MyTypedef)(float);
MyTypedef m = nullptr;
I'm guessing T deduces to int, and C deduces to MyClass. What "happens" to float?
That is a pointer to member, not necessarily a pointer to member function. The difference is that it can generate a pointer to member function or a pointer to non-function member.
Now in the particular use case, the destination is a pointer to member, the compiler is seeing an expression in which it needs a int (MyClass::*)(float), and on the other hand it has a nullptr. It tries to find a conversion and it finds the operator T C::*(), which is a valid conversion if C is deduced to be MyClass and T is deduced to be int (float) [function taking a float and returning an int].
I also find this particular corner of the language a bit confusing (having typedefs, or deduced types for functions), for example this is legal if weird:
typedef void int_f(int);
struct X {
int_f m;
};
void X::m(int x) { std::cout << x << '\n'; }
The same thing is going on in the conversion operator that you are concerned with.