16

I have two classes; Salary that is intended to hold information and calculations regarding the salary of an employee and Employee that has an object of type class Salary and some members like name and address of the employee...

  • What I want to do is to prevent class Salary from being instantiated except by class Employee. So I declared the constructors of Salary private and made Employee a friend of Salary. But I get errors:

    class Employee;
    
    class Salary {
        public:
    
        private:
            Salary() : revenue_{}, cost_{} {}
            Salary(int x, int y) : revenue_{ x },
            cost_{ y } {
    
            }
            int revenue_, cost_;
            friend class Employee;
    };
    
    class Employee {
        public:
            std::string name_;
            Salary sal;
    };
    
    int main(){
    
        Employee emp{}; // "Salary::Salary()" is inaccessible
    }
    
  • The problem goes away if I forward declare main:

    int main(int, char*[]);
    

    And make main a friend of class Salary like so in Salary:

    class Salary {
        //...
        friend int main(int argc, char* argv[]);
    };
    

Now the program compiles correctly!

*** Another thing in main if I declare an object this way:

Employee emp; // ok
Employee emp{}; // error?
Boann
  • 48,794
  • 16
  • 117
  • 146
Syfu_H
  • 605
  • 4
  • 17

4 Answers4

18

Because you don't provide a constructor for Employee the braces in your initialization Employee emp{}; will perform an aggregate initialization, which essentially means that each member is initialized one-by-one using the default rules, in the context of main(). Since main() doesn't have access to the Salary constructor, it fails.

As others have pointed out, adding an Employee default constructor will resolve your problem:

class Employee {
    public:
        Employee() = default;
        std::string name_;
        Salary sal;
};
zdan
  • 28,667
  • 7
  • 60
  • 71
  • 1
    I'm trying on MSVS and only `Employee() {};` allows `Employee emp{};` to compile. Clang seems to accept `Employee() = default;`, but then again, Clang [seems to accept](https://godbolt.org/z/Tebcbg) having no default constructor here. – wally Apr 23 '19 at 22:32
  • 1
    GCC [does the same as Clang](https://godbolt.org/z/-1LHP8), and doesn't need a default constructor to compile in this case. Have you tried this answer on any specific compiler? – wally Apr 23 '19 at 22:43
  • Thanks. it saves the day! – Syfu_H Apr 25 '19 at 07:50
4

You need Employee's ctor to call the ctor of Salary. The ctor of Salary is not accessible from main.

eg:

class Employee {
public:
    Employee() : sal() {}
    public:
        std::string name_;
        Salary sal;
};
Pavan Manjunath
  • 27,404
  • 12
  • 99
  • 125
schuess
  • 1,009
  • 1
  • 10
  • 21
1

If you erase the "{}" after "Employee emp" in your main() function it compiles just fine (gcc 7.3.1 on Fedora 27).

Eric Sokolowsky
  • 134
  • 2
  • 8
  • 4
    I recommend explaining why. – user4581301 Apr 23 '19 at 22:06
  • Yes. Not onyl GCC but also MSVC14 also compiles `Employee emp;` but why? – Syfu_H Apr 23 '19 at 22:07
  • 1
    @Syfu_H [Value Initialization](https://en.cppreference.com/w/cpp/language/value_initialization). And I could be mistaken here (been caught on this in the past), but the Value Initialization is being replaced by [Aggregate Initialization](https://en.cppreference.com/w/cpp/language/aggregate_initialization) – user4581301 Apr 23 '19 at 22:15
1

You have to explicitly declare the default constructor of class Employee thus you can initialize an abject via uniform initialization:

class Employee {
    public:
        Employee(){} // add it
        std::string name_;
        Salary sal;
};

int main(){
    Employee emp{}; // now this should compile

}
Raindrop7
  • 3,889
  • 3
  • 16
  • 27