I encountered this method and I'm not sure how to interpret it:
const char(*get_foo(int par))[38]
{
return foo;
}
the par parameter is not used in the function...
And foo is defined as:
static const char bar[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const char(*foo)[38] = &bar;
get_foo, foo, and bar are all members of the same class.
Also, when get_foo is called, its return value is stored in a const ushort* value:
const ushort* baz = *instant->get_foo(some_value);
I didn't see that syntax before and can't understand this method declaration, what are the different parts that make up this declaration and explain how to read it?
This is a function that takes an int and returns a pointer-to-array containing 38 const char types. It's not clear why the par parameter is not used, that would be a question for the original author.
A function like this should be read:
const char(*get_foo(int par))[38]
^~~~~~~~~~~~~~~~ (1)
^~~~~~~~~~~~ ^~~~~ (2)
The name of the function is get_foo, with function parameter int named par
The return type is const char(*)[38] -- which is a pointer to an array of char char containing 38 entries
Due to operator precedence in C++, it leads to types that look obfuscated like this, since otherwise it would introduce syntactic ambiguities. It would be easier to read had the author of the code simply used an alias:
using array_type = const char(*)[38];
array_type get_foo(int par);
which is equivalent to const char(*get_foo(int par))[38]
Related
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.
I have recently started using more C++11 features in my code and I have been wondering if the placement of the constexpr keyword makes difference whether it is before or after the constant's type.
Style 1:
constexpr int FOO = 1;
constexpr auto BAR = "bar";
Style 2:
int constexpr FOO = 1;
auto constexpr BAR = "bar";
Style 2 is the way I prefer to place the const keyword and placing constexpr the same way would bring some consistency to the code. However, is that considered bad practice or is there something else wrong with style 2 because I don't really see anyone writing it like that.
It's a specifier, like long, short, unsigned, etc. They can all be moved around. For example, the following are two equivalent and valid declarations:
int long const long unsigned constexpr foo = 5;
constexpr const unsigned long long int foo = 5;
By convention, though, constexpr would appear before the type name. It can confuse others if you put it after, but it is technically valid. Since constexpr serves a different purpose than const, I don't see the same benefit in putting it to the right. For example, you can't do int constexpr * constexpr foo. In fact, int constexpr * foo will not allow you to reassign foo, rather than apply to what foo points to, so putting it to the right can be misleading if you expect the same kind of semantics as const.
To summarize:
int constexpr foo = 0; // valid
int constexpr * constexpr foo = nullptr; // invalid
int* constexpr foo = nullptr; // invalid
int constexpr * foo = nullptr; // valid
constexpr int* foo = nullptr; // valid and same as previous
msdn defines the syntax as:
constexpr literal-type identifier = constant-expression;constexpr literal-type identifier { constant-expression };constexpr literal-type identifier(params );constexpr ctor (params);
Which says that you should use it on the left.
Edit:
New specifier The keyword constexpr is a declaration specifier; modify the
grammar in [ISO03, §7.1] as follows:
1 The specifiers that can be used in a declaration are
decl-specifier:
storage-class-specifier
type-specifier
function-specifier
friend
typedef
constexpr
An explanation is given for this specifiers here.
To sum up #chris' answer is right :)
Suppose I wanted to make my own reference ("smart pointer") type which is guaranteed to always refer to immutable data, rather than merely immutably-viewed data. In other words, data which can't be mutated by anyone, as opposed to just not through that particular reference. This is not so easy, because C++ generally considers const things to be a subcase of mutable-access things, and & references implicitly convert to const &, likewise * to const *.
Let's call this type:
template<typename T>
class ImmRef { ... };
One straightforward thing I can do is to declare:
template<typename T>
struct Imm
{
const T value;
};
and then only allow creating ImmRefs out of Imms, or other ImmRefs. Here the variable itself is declared const, so it's not possible to make any mutable references to it, and it is in fact truly immutable. (Unless it internally uses mutable, but since there's nothing we can do about that, let's ignore it.)
That works, but for greater flexibility, wider applicability, and compatibility with other code which doesn't know about our Imm type, it would be much better if we could create ImmRefs to any const-declared variable. But it's not clear whether C++ makes it possible to distinguish "a variable that is declared const" from "a const reference to a variable", or even from "a variable that is not declared const".
In other words, this should work:
const int myConstNumber = 666;
ImmRef<int> myImmRef = immRef(myConstNumber);
But this should not:
int myNumber = 666;
ImmRef<int> myImmRef = immRef(myNumber);
And this should not:
const int& myConstRef = myNumber;
ImmRef<int> myImmRef = immRef(myConstRef);
Is there any dark template magic which lets me do this?
There's no way to tell that a reference refers to a real const object. And there's no way for a function to tell whether its argument names an object or a reference.
decltype will tell you specifically that a name belongs to a const object (not a reference), but the name must be in scope. This usage can't be encapsulated in a template.
A macro will do the job, if you really really want it:
#define MAKE_IMMREF( NAME ) ImmRef< decltype( NAME ) >{ NAME }
template<typename T>
struct ImmRef {
static_assert ( std::is_const< T >::value, "ImmRef requires a constant." );
T & ref;
};
Demo.
I get a real kick out of exploring the unusual corners of C++. Having learned about the real types of functions rather than function pointers from this question, I tried messing around with function typing and came up with this bizarre case:
typedef int Func(int);
int Foo(int x) { return 1; }
int main()
{
const Func*& f = &Foo;
return 0;
}
Since &Foo is an rvalue of type Func*, I figured that I should be able to put it in a const reference, but I get this error from g++ 4.6:
funcTypes.cpp: In function ‘int main()’:
funcTypes.cpp:7:23: error: invalid initialization of non-const reference of type ‘int (*&)(int)’ from an rvalue of type ‘int (*)(int)’
But f is const! It has become apparent to me that the application of const to a function (or reference/reference to pointer etc.) is simply ignored; this code compiles just fine:
template <typename A, typename B>
struct SameType;
template <typename A>
struct SameType<A, A> { };
typedef int Func(int);
int main()
{
SameType<const Func, Func>();
return 0;
}
I'm guessing this is how boost pulls off their is_function type trait, but my question is - why does C++ allow this by ignoring it instead of forbidding it?
EDIT: I realise now that in the first example f is non-const and that const FuncPtr& f = &Foo does work. However, that was just background, the real question is the one above.
But f is const!
No, it's not. You're confusing
const Func*& f = &Foo;
with
Func* const& f = &Foo;
The former is a non-const ref to a const pointer. The latter is a const ref to a non-const pointer.
That's why I always write the const-ness before the */& rather than before the type. I would always write the first case as
Func const*& f = &Foo;
and then read right to left: reference to a pointer to a const Func.
In c++03 it was not ignored, but illformed (and was an sfinae case). I guess they changed that in c++11 because then you can simply have function parameters be const F& and can pass to it rvalue function objects aswell as normal functions.
See this DR which made the change http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#295
&Foo is a pointer. In general, I would suggest avoiding references to pointers (const or no). At least, not unless you know what you're doing.
So you should have:
const Func *f = &Foo;
Or really, you can ditch the const entirely:
Func *f = &Foo;
why does C++ allow this by ignoring it instead of forbidding it?
Because you're talking about two different things.
In C++, there is a difference between a function type and a function pointer. Foo is a function type, specifically int(int). &Foo is a function pointer, of type int(*)(int). A function type degrades into a function pointer, where necessary (much like array types degrade into pointers). But they are distinct (just like arrays).
So your two cases are not the same. Your first case is dealing with a function pointer, and your second case is dealing with a function type (which is what the template argument is deduced as).
As for why function types swallow the const, that's because the values of function types are already implicitly constant. You can't change them. The only operation you can perform on a function type is () (or conversion to function pointer). So a const T is equivalent to T if T is a function type. Visual Studio 2010 actually gives a warning about that.
The following compiles fine:
typedef int Func(int);
int Foo(int x) { return 1; }
int main()
{
Func* const& f = &Foo; //ok
return 0;
}
Be aware that const statements are evaluated along with pointers and references from right to left. The last const to the very left you wrote translates to last possible position right of a Name (C++ FAQ on const placement). Hence
const Func*& f
Is translated by the compiler to
Func const*& f
Hence you get a reference to a constant pointer which is not what you want. Besides I would not use references to function pointer if you do not really have to.
No, f is not const. First of all, it is a reference to some mutable type (that mutable type happens to be a const pointer, i.e. a mutable pointer to something that you promise not to change through that particular pointer). However, with &Foo you are creating a temporary (of type Func*) and you cannot assign a temporary to a mutable reference. You can only assign it to a const reference. And that is precisely what the error message is telling you.
Sometimes the error messages can be a bit cryptic.
I put together an example on ideone to illustrate the types printed by gcc for a variety of things:
#include <iostream>
typedef int(Func)(int);
typedef Func* FuncPtr;
typedef FuncPtr& FuncPtrRef;
typedef FuncPtr const& FuncPtrConstRef;
template <typename T> void DisplayType() { T::foo(); }
int main() {
DisplayType<Func>();
DisplayType<FuncPtr>();
DisplayType<FuncPtrRef>();
DisplayType<FuncPtrConstRef>();
}
The compilation errors give:
Func is of type int ()(int) (not a valid type, should now be fixed in gcc)
FuncPtr is of type int (*)(int)
FuncPtrRef is of type int (*&)(int)
FuncPtrConstRef is of type int (* const&)(int)
In your error message you have int (*&)(int), which is a reference to a non-const pointer to a function type.
You are not allowed to bind an rvalue to a non-const reference.
Note: this has thus nothing to do with const being swallowed, as smparkes correctly diagnosed
Say I have this example:
char const * const
foo( ){
/* which is initialized to const char * const */
return str;
}
What is the right way to do it to avoid the compiler warning "type qualifier on return type is meaningless"?
The way you wrote it, it was saying "the returned pointer value is const". But non-class type rvalues are not modifiable (inherited from C), and thus the Standard says non-class type rvalues are never const-qualified (right-most const was ignored even tho specified by you) since the const would be kinda redundant. One doesn't write it - example:
int f();
int main() { f() = 0; } // error anyway!
// const redundant. returned expression still has type "int", even though the
// function-type of g remains "int const()" (potential confusion!)
int const g();
Notice that for the type of "g", the const is significant, but for rvalue expressions generated from type int const the const is ignored. So the following is an error:
int const f();
int f() { } // different return type but same parameters
There is no way known to me you could observe the "const" other than getting at the type of "g" itself (and passing &f to a template and deduce its type, for example). Finally notice that "char const" and "const char" signify the same type. I recommend you to settle with one notion and using that throughout the code.
In C, because function return values, and qualifying values is meaningless.
It may be different in C++, check other answers.
const int i = (const int)42; /* meaningless, the 42 is never gonna change */
int const foo(void); /* meaningless, the value returned from foo is never gonna change */
Only objects can be meaningfully qualified.
const int *ip = (const int *)&errno; /* ok, `ip` points to an object qualified with `const` */
const char *foo(void); /* ok, `foo()` returns a pointer to a qualified object */
None of the previous answers actually answer the "right way to do it" part of the question.
I believe that the answer to this is:
char const * foo( ){
which says you are returning a pointer a constant character.