10

I like to declare even value parameters as const where possible, and by searching SO, I found that that's not too uncommon. Like this:

int add(const int a, const int b)
{
    ...
}

But I'm wondering: const for values is an implementation detail of my function, not part of it's interface. So putting it into the prototype seems unnecessary.

This prototype for the above function seems to work just fine:

int add(int a, int b);

Yet I've heard about issues that e.g. declaring the main function's argc as const can lead to problems:

int main(const int argc, const char* const argv[])

So does that mean that int add(int a, int b) and int add(const int a, const int b) are not identical after all?

And if it's technically OK, is that something I should do? I could also leave out variable names in the prototype, but I don't, so maybe I shouldn't leave out const either?

Community
  • 1
  • 1
futlib
  • 8,258
  • 13
  • 40
  • 55
  • 1
    Don't leave out the names on the interface, by the way. When people are looking to use your function that's where they'll look first (sometimes the definition won't even be available depending on how your code is organized or distributed). The names there are the only documentation, sometimes (and unfortunately). – GManNickG Mar 08 '13 at 15:56
  • With `int add(int a, int b);` the compiler has enough to generate the correct code for calls to any function with compatible parameters. If you then define `int add(const int a, const in b) {}` that's compatible, and the declaration is valid. – teppic Mar 08 '13 at 16:03

3 Answers3

11

It's not OK for the function type to differ, but you need to know what is part of the function type and what isn't. In your case, the const for the parameters is not significant, so the function type is the same, although the declaration looks like it differs from the definition.

In your case it's

8.3.5 Functions [dcl.fct]

5 A single name can be used for several different functions in a single scope; this is function overloading (Clause 13). All declarations for a function shall agree exactly in both the return type and the parameter-type-list. 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 “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. 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 ]

As it seems, it need some explanation, so here we go: The important sentence in our case is: After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type.

This means that all top-level cv-qualifier are remove. To explain what top-level means, I'll write types in an illegal way to emphasize what a const refers to:

  • const int = (const (int)) -> this is a top-level const
  • const int* = ((const (int))*) -> not top-level, it's at the second level
  • const int* const = (((const (int))*) const) -> the second const is at top-level
  • const int& = ((const (int))&) -> not top-level

I hope this clears some misconceptions about function types up.

For your other questions: I'd advise to keep the declaration and the definition identical, as it might confuse people (like evidenced by this question ;).

For the example of main that you gave:

int main( const int argc, const char* const argv[] )

is, according to the above quote from the standard, equivalent to:

int main( int argc, const char* const* argv )

so the added const for argv does not end up as a top-level const which is removed and it's therefore an ill-formed function type for main, which expects:

int main( int argc, char** argv )

You last question about leaving out the parameter names: I wouldn't do it because to me, they are part of the documentation of the function. They communicate the intent and the semantics of the function (if you choose them wisely).

Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • 2
    What are you talking about? `const` is very significant in the signature. – Tony The Lion Mar 08 '13 at 15:40
  • 5
    @LuchianGrigore: In some cases (like the above) it is **not** part of the signature. Get the facts straight before downvoting. – Daniel Frey Mar 08 '13 at 15:53
  • @juanchopanza plan on checking. – Luchian Grigore Mar 08 '13 at 15:55
  • 1
    @LuchianGrigore you can try defining both `void foo(int)` and `void foo(const int)` and get a re-definition error. I can't check the standard right now unfortunately. – juanchopanza Mar 08 '13 at 15:57
  • I'm guessing this *After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type.* is the line that explains this? I don't really understand that though. – Tony The Lion Mar 08 '13 at 16:03
  • 3
    @TonyTheLion: Yes, that is it. Note that is says *top-level*, meaning that `const int` is reduced to `int`, but `const int*` stays as it is. Also: `const int* const` becomes `const int*`. – Daniel Frey Mar 08 '13 at 16:04
  • ah, I guess I don't really know what *top level* is supposed to mean then? – Tony The Lion Mar 08 '13 at 16:05
  • 3
    Sorry for the rash downvote (+1 now), it's just one of those cases where you think you're right, then bam!, C++ says no :) – Luchian Grigore Mar 08 '13 at 16:06
  • @TonyTheLion that's right. It means `const int` is the same as `int` but `const int*` is *not* the same as `int*`. – juanchopanza Mar 08 '13 at 16:06
  • You learn something new everyday... +1. Still, I think it's *bad practice* to do this, even if explicitly allowed by the standard. – Nik Bougalis Mar 08 '13 at 16:35
  • @NikBougalis: Why? I mean, I wouldn't purposefully delete the top-level cv-qualifiers from my declarations but I wouldn't purposefully make sure they all match either. – GManNickG Mar 08 '13 at 16:48
  • Exactly because this is an *esoteric* feature of the language that most people are unaware of *and* which provides no real tanglible benefit (in my opinion). Plus, the way I see it is that if you show someone a picture of what they must buy at the grocery store, the picture should be accurate - don't show a red pepper when you really want a green one. – Nik Bougalis Mar 08 '13 at 16:53
  • @NikBougalis I find it useful to `const` my value types, so that *all* parameters which I don't wish to modify will be const. It happens in the real life that the innocent `int a` parameter can be erroneously overwritten somewhere in function. – Red XIII Mar 08 '13 at 17:14
  • @RedXIII And if that's your thing, that's fine - my point is be consistent between the declaration and the definition. – Nik Bougalis Mar 08 '13 at 17:23
  • @NikBougalis: To the caller, `const int` and `int` are *exactly the same*. There is no different picture. – GManNickG Mar 08 '13 at 17:27
  • OK, now I know that it's legal :) But I'm still wondering: Is it a good idea? Whether that's legal spawned quite the discussion here, not sure I want to alienate people like that. – futlib Mar 09 '13 at 04:54
  • I already gave you my *opinion/advice* on that (see in answer), but there is no definite answer and subjective discussion are discouraged on SO. Overall, I think you have enough information to make your *own* decision now. – Daniel Frey Mar 09 '13 at 09:00
1

It should be OK to have differing top level const for function parameters between declarations and definitions of the function, but note that not all compilers are bug free. For example, the Oracle Sun compiler has a long standing issue where it mangles int f(int) and int f(const int) differently.

To avoid any possibility of confusion that you really meant to pass by const reference I usually recommend avoiding top level const in public function declarations and to avoid possible compiler issues I would avoid it in function definitions as well.

(Note that changing char ** to const char* const argv[] in an argument list is not adding top level const, it's a genuine signature change. char** would only be equivalent to char** const in a function parameter list.)

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • But do you otherwise const everything you can? It feels inconsistent if I don't const parameters, but I'm not convinced that compiler issues and verbose interfaces aren't too much of a price to pay. – futlib Mar 09 '13 at 05:03
  • @futlib: I use `const` where it is appropriate and has a practical benefit; I don't necessarily "const everything I can". – CB Bailey Mar 09 '13 at 12:17
0

const for values is an implementation detail of my function, not part of it's interface.

That's the flaw in your thinking, when talking about references and pointers const has everything to do with the interface, it tells the programmer using the interface that what you're passing won't be altered by the function. It tells the compiler the same and binds the programmer to this contract.

A function with a const argument and one with a non-const argument are different.

Passing by value however, will copy the arguments, in which case altering them isn't an issue. Your ints are passed by value, where this const won't make much difference.

However, personally I don't think that is a reason to abuse having inconsistent interfaces, and make interfaces different in one place to another.

Whereas passing by ref, a ref to const is quite different to ref to non-const.

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • 1
    Although when you're passing by value, the function won't alter what you're passing anyway... – Andy Prowl Mar 08 '13 at 15:40
  • 3
    That's not true. Values are always copied, and the caller cannot possibly be affected by the constness of said value because it cannot access it. It's purely implementation detail. – GManNickG Mar 08 '13 at 15:40
  • 1
    Yup const on values is irrelevant. – Cat Plus Plus Mar 08 '13 at 15:40
  • Where you get confused is what `const` applies to in references and pointers. – Cat Plus Plus Mar 08 '13 at 15:41
  • It makes a whole lot of difference. It's part of the function signature, and it **expresses intent**. +1 – Luchian Grigore Mar 08 '13 at 15:42
  • 2
    @LuchianGrigore: OK, but what intent? Whether or not the function will internally modify a *copy* of the value it gets is irrelevant to the client... – Andy Prowl Mar 08 '13 at 15:43
  • Sorry, -1ing this. It doesn't answer the question. – GManNickG Mar 08 '13 at 15:44
  • @AndyProwl to the client, yes. To the implementer or future maintainers, no. – Luchian Grigore Mar 08 '13 at 15:44
  • @GManNickG I miscommunicated my intent, I didn't mean abuse `const`, I meant abusing having inconsistent interfaces. ie the declaration has `const` and the definition not, or some such insanity – Tony The Lion Mar 08 '13 at 15:46
  • @LuchianGrigore: I always thought the signature is meant to establish a contract with clients, is that a bad thinking? – Andy Prowl Mar 08 '13 at 15:46
  • @TonyTheLion: I see, you are correct. But it still doesn't say why this is okay: `void foo(int); int main() { foo(5); } void foo(const int){}` and in fact seems it imply such a thing shouldn't compile. Questioner wants to know why and when this is okay. – GManNickG Mar 08 '13 at 15:47
  • I DID say I meant const for values. I'm aware that it's not an implementation detail for references and pointers. – futlib Mar 08 '13 at 15:49
  • @GManNickG my only answer to that can be because it's value semantics and const doesn't matter then. – Tony The Lion Mar 08 '13 at 15:49
  • 2
    @LuchianGrigore: Btw, [this one](http://liveworkspace.org/code/3FDYza$19) compiles... so it seems top-level cv on value types does not really matter. – Andy Prowl Mar 08 '13 at 15:50
  • @TonyTheLion: Awfully informal reasoning though. :) I'm curious myself along with OP what in the standard allows this. – GManNickG Mar 08 '13 at 15:50
  • @GManNickG shall I just delete my answer? – Tony The Lion Mar 08 '13 at 16:03
  • @TonyTheLion: Well that's up to you. Other people share your sentiment about it being a bad idea, so you could leave a comment on the other answer, or make your answer CW, or leave it as is. Just depends how much "oomph" you want to present your text with. :) – GManNickG Mar 08 '13 at 16:47