0

I'm getting back into writing some C++ and I'm honestly rusty. I get the feeling I'd find a quick answer to my question if I just knew how to phrase it properly, but still I'd appreciate your help.

sanitycheck.cpp:

#include <string>    
using namespace std;

typedef struct STR_1 {    
  int val_a, val_b;

  STR_1 (int a, int b)
  { val_a = a; val_b = b; }    
} STR_1;

typedef struct STR_2{    
  string name;
  STR_1 myStr1;

  STR_2 (string n, STR_1 s)
  { name=n; myStr1 = s; }
} STR_2;

int main(){

  return 0;
} // end main

When I try to compile with g++ -o sanitycheck ./test/sanitycheck.cpp I get the following,

./test/sanitytest.cpp: In constructor ‘STR_2::STR_2(std::string, STR_1)’:
./test/sanitytest.cpp:25:3: error: no matching function for call to ‘STR_1::STR_1()’
   { name=name; myStr1 = &s; }
   ^
./test/sanitytest.cpp:25:3: note: candidates are:
./test/sanitytest.cpp:11:3: note: STR_1::STR_1(int*, int*)
   STR_1 (int *a, int *b)
   ^
./test/sanitytest.cpp:11:3: note:   candidate expects 2 arguments, 0 provided
./test/sanitytest.cpp:7:16: note: STR_1::STR_1(const STR_1&)
 typedef struct STR_1 {
                ^
./test/sanitytest.cpp:7:16: note:   candidate expects 1 argument, 0 provided
./test/sanitytest.cpp:25:23: error: no match for ‘operator=’ (operand types are ‘STR_1’ and ‘STR_1*’)
   { name=name; myStr1 = &s; }
                       ^
./test/sanitytest.cpp:25:23: note: candidate is:
./test/sanitytest.cpp:7:16: note: STR_1& STR_1::operator=(const STR_1&)
 typedef struct STR_1 {
                ^
./test/sanitytest.cpp:7:16: note:   no known conversion for argument 1 from ‘STR_1*’ to ‘const STR_1&’

One thing I'm not clear on is why would STR_1 myStr1; of STR_2 need to call the STR_1 constructor in the first place? Couldn't I initialize both types with,

int main()
{
    STR_1 bob = STR_1(5,6);
    STR_2 tom = STR_2('Tom',bob);
    return 0;
}

Thanks!

darkpbj
  • 2,892
  • 4
  • 22
  • 32

1 Answers1

2

Unless it is not already clear from the link in the comments to the OP: this here,

typedef struct STR_2{    
  string name;
  STR_1 myStr1;

  STR_2 (string n, STR_1 s)  // here the myStr1 default constructor is called
  { name=name; myStr1 = s; }
} STR_2;

requires STR_1 to be default constructible. In order to work around, you have to construct the member STR_1 myStr1; in the constructor's initializer list:

  STR_2 (string n, STR_1 s) : name(n), myStr1(s) {}

DEMO

This calls the compiler generated copy-constructor of STR_1 instead of the default constructor (the automatic generation of which is suppressed by providing a custom constructor).


Another option would be to use a pointer to STR_1:

typedef struct STR_2{    
  string name;
  std::unique_ptr<STR_1> myStr1;

  STR_2 (string n, STR_1 s)
  { name=name; myStr1 = std::make_unique<STR_1>(s); }  //just for the sake of explanation
                                                       //again, this would be better
                                                       //done in the initializer list
} STR_2;

Yet, I would depart from the first alternative only for a good reason.

davidhigh
  • 14,652
  • 2
  • 44
  • 75
  • Thanks @davidhigh! Accepted for answering the question, upvoted for a solid explanation of how/why it works. – darkpbj May 27 '15 at 15:20