0

I am trying to build a small program in C++, to learn the preprocessor directives and how they actually work.

The program is made up of 5 files: main.cpp, file1.h, file1.cpp, file2.h and file2.cpp

In file1.h, I declared 1 function that has the default parameter, and a new type byte:

typedef unsigned char byte;

byte f1(byte a, byte b = 5);

In file1.cpp, I defined it:

#include "file1.h"

byte f1(byte a, byte b) {

    return a + b;
}

In file2.h, I declared a second function that uses f1(), and always passes 10 to it as a second argument:

#include "file1.h"

byte f2(byte a);

And the same, in file2.cpp, I defined it:

#include "file2.h"

byte f2(byte a) {

    return f1(a, 10);
}

And finally, here is the main file:

#include <iostream>
using namespace std;

#include "file1.h"

int main() {

    cout << f1(3) << endl;

    return 0;
}

For now, all is Ok, and the output is simply 8.

But suppose I need to use the f2() function in my main file, and for that I included the file2.h, so the main file is now:

#include <iostream>
using namespace std;

#include "file1.h"
#include "file2.h"

int main() {

    cout << (int) f1(3) << endl;

    cout << (int) f2(2) << endl;

    return 0;
}

The compiler is giving this error: Error C2572 'f1': redefinition of default argument: parameter 1

Since file1.h is included in file2.h, now f1() is redeclared in file2.h with the b paramter also set to 5.

What can I do to prevent the redefinition, if we assume that I can not move the f2() declaration and definition to file1.h and file1.cpp respectively?

Note: I know that I could use #pragma once directive, but I am trying to solve this problem without it, since I am trying to learn the C++ directives professionally.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
X Y
  • 233
  • 1
  • 8
  • 1
    The `#include "file1.h"` statement does not belong in `file2.h`, since nothing in `file2.h` is dependent on what is declared in `file1.h` . That statement should be moved inside of `file2.cpp` instead, where the content of `file1.h` (ie, the declaration of `f1()`) is actually being used. – Remy Lebeau Dec 12 '22 at 21:29
  • @Remy Lebeau Yes, my problem was solved with your proposal, but I want to apologize for forgetting to introduce a dependency from `file2.h` to `file1.h`, because that how it is in my real case. I have edited the question, and replaced `int` by `byte` as a return type. – X Y Dec 12 '22 at 22:52

1 Answers1

1

In the code shown, byte and f1() are being declared multiple times in main.cpp when file1.h and file2.h are both #include'd.

Per the C++ standard, §8.3.6 [dcl.fct.default]/4:

[Note 2: A default argument cannot be redefined by a later declaration (not even to the same value) ([basic.def.odr]). — end note]

Which is exactly what is happening here.

Note: I know that I could use #pragma once directive, but I am trying to solve this problem without it, since I am trying to learn the C++ directives professionally.

Making your .h files have proper header guards (see #pragma once vs include guards?) is the correct and professional way to avoid redeclarations, eg:

file1.h:

#ifndef File1H
#define File1H

typedef unsigned char byte;

byte f1(byte a, byte b = 5);

#endif

file2.h:

#ifndef File2H
#define File2H

#include "file1.h"

byte f2(byte a);

#endif

Online Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I agree to this, but there just one thing, in `f1()`, if you make `b` a non-default parameter, it will compile normally without any `#pragma once` or preprocessor directive. So why does in the case of a default parameter it need all that, whereas it needs nothing in the case of normal parameters? – X Y Dec 13 '22 at 10:54
  • 1
    @XY because the C++ standard doesn't allow default parameters in multiple declarations. See [this answer](https://stackoverflow.com/a/6708956/65863) to [Multiple declarations of C++ functions with default parameters](https://stackoverflow.com/questions/6708726/) – Remy Lebeau Dec 13 '22 at 17:01
  • That is okay! And just as a remark, this can solve the problem: `int f(int a, int b #ifndef FILE1_H #define FILE1H = 5 #endif);` The preprocessor directives can simply be used around the default parameter value. – X Y Dec 13 '22 at 17:18
  • @XY that is a *terrible* solution. For one thing, it has a typo in it. For another, it will completely disable `file1.h` if proper header guards are implemented later. – Remy Lebeau Dec 13 '22 at 17:20