Invalid conversion from const &parameter to &parameter seems to be nonsense? - c++

I am just curious. I am passing pointer to function with signature
void printCommandReceived(const CommandDescriptor &descriptor)
as third parameter to constructor with signature
CommandLogFilter::CommandLogFilter(QSharedPointer<LogServer> logServer, QObject *parent,
void (*preprocessValidCommand)(CommandDescriptor &descriptor))
and getting error from g++ compiler:
error: invalid conversion from ‘void (*)(const CommandDescriptor&)’ to ‘void (*)(CommandDescriptor&)’ [-fpermissive]
In my understanding the reference to non-const object should be usable as argument to reference to const object parameter. So parameter with type pointer to function accepting non-const object reference should be more than satisfied with (and do implicit conversion from) argument of type pointer to function, which accepts even const object reference.
Where am I wrong?

void (*)(const CommandDescriptor&) and void (*)(CommandDescriptor&) are two completely different, unrelated types.
There are very simple rules regarding const: X* can be converted to X const*, X** can be converted to X const * const * and so on. Same thing with references. Nothing else is allowed.
Note that the rules do not allow to arbitrarily add or remove const at any position in the type, for example, X** cannot be converted to X const **. This is true also for the position of function arguments: you just cannot add or remove const there to get a compatible type.
Could these rules be extended so that they accommodate cases like yours and remain consistent? Probably so. But they are not.

C++ has a limited set of situations where const can be added or removed implicitly. You ran into one where it cannot be done. The reason why not is probably as simple as "describing those cases which are safe would be hard, and standards writers are lazy and conservative".
As a work around, you can do this:
CommandLogFilter bob(
logServer,
parent,
[](CommandDescriptor &descriptor) {
return printCommandReceived(descriptor);
}
);
as stateless lambdas can implicitly convert-to a pointer to a function matching their signature.
I don't like having to make the signature explicit there, but there is no way to do something similar with template "auto" lambdas and have the signature deduced unfortunately.

Related

how to correct move unique_ptr in lambda? [duplicate]

I want to pass a raw pointer inside lambda, but I don't want it to be leaked, if the lambda isn't invoked. It looks like this:
void Clean(std::unique_ptr<int>&& list);
void f(int* list) {
thread_pool.Push([list = std::unique_ptr<int>(list) ] {
Clean(std::move(list)); // <-- here is an error.
});
}
I get an error in Clang 3.7.0:
error: binding of reference to type 'unique_ptr<[2 * ...]>' to a value of type 'unique_ptr<[2 * ...]>' drops qualifiers
But I don't see any qualifiers at the first place, especially dropped.
Also, I found similar report on the mailing list, but without answer.
How should I modify my code, so it gets compiled and works as expected by semantics?
You need to make the inner lambda mutable:
[this](Pointer* list) {
thread_pool.Push([this, list = std::unique_ptr<int>(list) ]() mutable {
^^^^^^^^^
Clean(std::move(list));
});
};
operator() on lambdas is const by default, so you cannot modify its members in that call. As such, the internal list behaves as if it were a const std::unique_ptr<int>. When you do the move cast, it gets converted to a const std::unique_ptr<int>&&. That's why you're getting the compile error about dropping qualifiers: you're trying to convert a const rvalue reference to a non-const rvalue reference. The error may not be as helpful as it could be, but it all boils down to: you can't move a const unique_ptr.
mutable fixes that - operator() is no longer const, so that issue no longer applies.
Note: if your Clean() took a unique_ptr<int> instead of a unique_ptr<int>&&, which makes more sense (as it's a more explicit, deterministic sink), then the error would have been a lot more obvious:
error: call to deleted constructor of `std::unique_ptr<int>`
note: 'unique_ptr' has been explicitly marked deleted here
unique_ptr(const unique_ptr&) = delete
^

Are some usages of const only really useful when returning or passing things by reference? Or do they have subtle uses I'm not seeing

I've read about the various places to put const. Some usages seem clearly useful to me. Others however evade me. It would be really helpful if someone could confirm or correct my understanding as I explain my mental model of things.
These are the same. I'm not sure I understand why this would ever be useful, though. Does it perhaps allow one to initialize const int variables with a function, which in turn allows some compiler optimizations?
const int foo();
int const foo();
These are the same. The returned pointer cannot be used (via dereferencing) to change the values pointed to.
const int * foo();
int const * foo();
This means the returned pointer itself cannot be changed. But, why would it matter if the caller essentially decides to ignore the returned pointer and set it to something else? Is this only really useful if the pointer is returned by reference?
int * const foo();
These are the same. It means you can only pass in const ints, which allows the compiler to optimize things.
int foo(const int foo);
int foo(int const foo);
This means the passed-in pointer cannot be changed. I'm wondering here too, why would it matter unless the pointer is being passed in by reference?
int foo(int * const foo);
This (as a member function) guarantees that the function won't change the state of the object. Also, if the object itself is declared const, then it will only be able to call such functions.
int foo(int foo) const;
const int as return type is pointless, because in an expression the type of a non-class prvalue result of a function call will be stripped of its top-level const anyway. There is no distinction between a const and non-const prvalue of a non-class type. So the type of foo() is just int, no matter whether it is declared to return const int or int.
It would be different if the return type was a const qualified class type, e.g. const std::string foo(). In that case the const would disallow calling non-const-qualified member functions directly on the result of foo(), e.g. foo().resize(42). Still, this is a very rarely used distinction. And as noted in the comments, under certain conditions it can prevent move operations. E.g. in the above if we have a std::vector<std::string> v;, then v.push_back(foo()) will cause a copy, rather than a move, of the returned string into the vector.
However, the const qualifier is part of the return type in the function type and therefore it is technically possible to differentiate a function declared with const return type from one without it. The type of int foo(int foo) is int(int), but the type of const int foo(int foo) is const int(int). (However overloading based on return type is not possible for non-template functions anyway. The return type is not part of the function signature.)
correct
Same as 1. The type of foo() is simply int*.
The top-level const in the function parameter does not affect the type or signature of the function (in contrast to 1. where it does affect the type). So int foo(const int foo); and int foo(int foo); declare the same function and both have type int(int). Top-level const also doesn't affect how a variable can be initialized, so it doesn't make sense to say "you can only pass in const int". There are no const-qualified prvalues of type int anyway and if int foo can be initialized from some expression, then so can const int foo. The const has no implication on initialization or overload resolution. However const can be used like this in a function definition to tell the compiler and yourself that this parameter is not intended to be modified in the definition.
Same as 4.
This is the correct idea, although in the details it is not strictly true. Rather the const is only relevant to overload resolution (behaving as if the implicit object parameter was a const reference) and the type of this (which will be a pointer to const). It is still possible to mutate members declared as mutable or to use const_cast to mutate members. It is also not relevant whether the object itself is const, only whether the glvalue through which the member function is called is.

Assigning function to function pointer, const argument correctness?

I am learning the basics of C++ and OOP in my university now. I am not 100% sure how a function pointer works when assigning functions to them. I encountered the following code:
void mystery7(int a, const double b) { cout << "mystery7" << endl; }
const int mystery8(int a, double b) { cout << "mystery8" << endl; }
int main() {
void(*p1)(int, double) = mystery7; /* No error! */
void(*p2)(int, const double) = mystery7;
const int(*p3)(int, double) = mystery8;
const int(*p4)(const int, double) = mystery8; /* No error! */
}
From my understanding, the p2 and p3 assignments are fine as the function parameters types match and const-ness is correct. But why don't the p1 and p4 assignments fail? Shouldn't it be illegal to match const double/int to non-const double/int?
According to the C++ Standard (C++ 17, 16.1 Overloadable declarations)
(3.4) — Parameter declarations that differ only in the presence or
absence of const and/or volatile are equivalent. That is, the const
and volatile type-specifiers for each parameter type are ignored when
determining which function is being declared, defined, or called.
So in the process of determining of the function type the qualifier const for example of the second parameter of the function declaration below is discarded.
void mystery7(int a, const double b);
and the function type is void( int, double ).
Also consider the following function declaration
void f( const int * const p );
It is equivalent to the following declaration
void f( const int * p );
It is the second const that makes the parameter constant (that is it declares the pointer itself as a constant object that can not be reassigned inside the function). The first const defines the type of the pointer. It is not discarded.
Pay attention to that though in the C++ Standard there is used the term "const reference" references themselves can not be constant opposite to pointers. That is the following declaration
int & const x = initializer;
is incorrect.
While this declaration
int * const x = initializer;
is correct and declares a constant pointer.
There is a special rule for function arguments passed by value.
Although const on them will affect their usage inside the function (to prevent accidents), it's basically ignored on the signature. That's because the constness of an object passed by value has no effect whatsoever on the original copied-from object at the call site.
That's what you're seeing.
(Personally I think that this design decision was a mistake; it's confusing and unnecessary! But it is what it is. Note that it comes from the same passage that silently changes void foo(T arg[5]); into void foo(T* arg);, so there's plenty of hokey bullsh!t in there already that we have to deal with!)
Do recall, though, that this doesn't just erase any const in such an argument's type. In int* const the pointer is const, but in int const* (or const int*) the pointer is non-const but is to a const thing. Only the first example relates to constness of the pointer itself and will be stripped.
[dcl.fct]/5 The type of a function is determined using the following rules. The type of each parameter (including function parameter packs) is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or of function type T is adjusted to be “pointer to T”. After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function's parameter-type-list. [ Note: This transformation does not affect the types of the parameters. For example, int(*)(const int p, decltype(p)*) and int(*)(int, const int*) are identical types. — end note ]
There is a situation where adding or removing a const qualifier to a function argument is a serious bug. It comes when you pass an argument by pointer.
Here’s a simple example of what could go wrong. This code is broken in C:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// char * strncpy ( char * destination, const char * source, size_t num );
/* Undeclare the macro required by the C standard, to get a function name that
* we can assign to a pointer:
*/
#undef strncpy
// The correct declaration:
char* (*const fp1)(char*, const char*, size_t) = strncpy;
// Changing const char* to char* will give a warning:
char* (*const fp2)(char*, char*, size_t) = strncpy;
// Adding a const qualifier is actually dangerous:
char* (*const fp3)(const char*, const char*, size_t) = strncpy;
const char* const unmodifiable = "hello, world!";
int main(void)
{
// This is undefined behavior:
fp3( unmodifiable, "Whoops!", sizeof(unmodifiable) );
fputs( unmodifiable, stdout );
return EXIT_SUCCESS;
}
The problem here is with fp3. This is a pointer to a function that accepts two const char* arguments. However, it points to the standard library call strncpy()¹, whose first argument is a buffer that it modifies. That is, fp3( dest, src, length ) has a type that promises not to modify the data dest points to, but then it passes the arguments on to strncpy(), which modifies that data! This is only possible because we changed the type signature of the function.
Trying to modify a string constant is undefined behavior—we effectively told the program to call strncpy( "hello, world!", "Whoops!", sizeof("hello, world!") )—and on several different compilers I tested with, it will fail silently at runtime.
Any modern C compiler should allow the assignment to fp1 but warn you that you’re shooting yourself in the foot with either fp2 or fp3. In C++, the fp2 and fp3 lines will not compile at all without a reinterpret_cast. Adding the explicit cast makes the compiler assume you know what you’re doing and silences the warnings, but the program still fails due to its undefined behavior.
const auto fp2 =
reinterpret_cast<char*(*)(char*, char*, size_t)>(strncpy);
// Adding a const qualifier is actually dangerous:
const auto fp3 =
reinterpret_cast<char*(*)(const char*, const char*, size_t)>(strncpy);
This doesn’t arise with arguments passed by value, because the compiler makes copies of those. Marking a parameter passed by value const just means the function doesn’t expect to need to modify its temporary copy. For example, if the standard library internally declared char* strncpy( char* const dest, const char* const src, const size_t n ), it would not be able to use the K&R idiom *dest++ = *src++;. This modifies the function’s temporary copies of the arguments, which we declared const. Since this doesn’t affect the rest of the program, C doesn’t mind if you add or remove a const qualifier like that in a function prototype or function pointer. Normally, you don’t make them part of the public interface in the header file, since they’re an implementation detail.
¹ Although I use strncpy() as an example of a well-known function with the right signature, it is deprecated in general.

C++11: Abstracting over const, volatile, lvalue reference, and rvalue reference qualified member function pointers?

C++03 lets you qualify function parameters as being const, volatile, and/or lvalue references (&).
C++11 adds one more: rvalue references (&&).
Furthermore, C++ lets you overload functions based on the qualifiers of their parameters, so that the most appropriate overload is selected when calling the function.
A member function can conceptually be thought of as a function which takes an extra parameter, whose type is a reference to an instance of the class of which it is a member. It's possible to overload a member function based on the qualifiers of this 'extra parameter' in much the same way as any other parameter. This is expressed by putting the qualifiers at the end of the function signature:
struct Foo
{
int& data(); // return a non-const reference if `this` is non-const
const int& data() const; // return a const reference if `this` is const
};
In C++03, const and volatile qualifiers are possible, and C++11 also allows & and && (& could theoretically have been allowed in C++03, but it wasn't).
Any combination of qualifiers can be used, with the exception that & and && are mutually exclusive, which makes for 2^2 = 4 possibilities in C++03 and 2^4-4 = 12 in C++11.
This can be quite a pain when you want to work with member function pointers, because they aren't even a little bit polymorphic in these qualifiers: the qualifiers on the "this type" of a member function pointer passed as an argument must exactly match those on the type of the parameter it's being passed as. C++ also offers no explicit facility to abstract over qualifiers. In C++03 this was mostly OK, because you would have to write a const version and a non-const version and no one cares about volatile, but in the pathological case in C++11 (which is not as uncommon as it is pathological) you could have to manually write as many as 12 overloads. Per function.
I was very happy to discover that if you are passing the type of the enclosing class as a template parameter and derive the type of a member function pointer from it, that const and volatile qualifiers are allowed and propagated as you would expect:
template<typename Object>
struct Bar
{
typedef int (Object::*Sig)(int);
};
Bar<Baz>; // Sig will be `int (Baz::*)(int)`
Bar<const Baz>; // Sig will be `int (Baz::*)(int) const`
Bar<volatile Baz>; // Sig will be `int (Baz::*)(int) volatile`
Bar<const volatile Baz>; // Sig will be `int (Baz::*)(int) const volatile`
This is a great deal nicer than having to write out all of the cases manually.
Unfortunately, it doesn't seem to work for & and &&.
GCC 4.7 says:
error: forming pointer to reference type ‘Baz&&’
But that's not too surprising, given that GCC as of 4.7 doesn't yet have support for reference qualifiers on this.
I also tried it with Clang 3.0, which does have such support:
error: member pointer refers into non-class type 'Baz &&'
Oh, well.
Am I correct in concluding that this is not possible, and that there's no way to abstract over reference qualifiers on the "this type" of member function pointers? Any other techniques for abstracting over qualifiers (especially on this) other than in the specific case when you're passing the "this type" as a template parameter would also be appreciated.
(It's worth pointing out that if C++ didn't distinguish between member functions and normal functions, this would all be trivial: you'd use the template parameter as the type of a parameter of the function (pointer), and the template argument would be passed through as-is, qualifiers intact, no extra thought necessary.)
Have you thought about simply specializing your template ?
You can just add the two versions:
template <typename Object>
struct Bar<Object&> {
typedef int (Object::*Sig)(int)&;
};
template <typename Object>
struct Bar<Object&&> {
typedef int (Object::*Sig)(int)&&;
};
And then the compiler will pick the right specialization (or fallback to the general case) appropriately.
This saves you from the const/volatile thing, but does imply that you need to write the code 3 times.

passing a string literal to a function that takes a std::string&

I have the following function
void AddNodeValue(XMLNode& node, std::string& value);
I want to use it like this:
document.AddNodeValue(modvalue,"modvalue");
and the compiler complains:
error C2664: 'void XML::XMLDocument::AddNodeValue(XML::XMLNode &,std::string &)' : cannot convert parameter 2 from 'const char [9]' to 'std::string &'
A reference that is not to 'const' cannot be bound to a non-lvalue
I don't get why that is wrong?
Compiler: VS2003
Your function needs to take const std::string& for you to use it like that.
C++ has a rule that an rvalue (in your case, a temporary std::string which is created from the string literal) can be bound with a const reference, but not a non-const reference.
As far as I know, this restriction isn't due to any fundamental implementation problem, since temporary values can be modified in other ways. But a function which takes a non-const reference is assumed to do so because its main purpose is to modify that argument. It doesn't usually make a lot of sense to do that with a temporary, so possibly banning it catches errors far more than it prevents people doing something worthwhile. Anyway, not all rvalues are temporaries: some are literals which really cannot be modified.
If you can't change the function AddNodeValue, then you can work around it:
std::string valstr("modvalue");
document.AddNodeValue(modvalue, valstr);
// valstr might have changed, check the documentation of AddNodeValue