Calling overloaded constructor from constructor initialisation list
In the code below, my intent is to call one of two overloaded constructors for the kap
(class opacity
) based on what arguments are passed to the object of class material
:
class opacity{
private:
int mode;
double kap_const;
double kappa_array[10][10];
public:
opacity(double constkap); // picking the constructor sets the mode
opacity(char* Datafile);
double value(double T, double P); // will return a constant or interpolate
};
opacity::opacity(double constkap):mode(1){
kap_const = constkap;
}
opacity::opacity(char* Datafile):mode(2){
// read file into kappa_array...
}
class Matter {
public:
Matter(int i, double k, char* filename); // many more values are actually passed
opacity kap;
int x; // dummy thing
// more variables, call some functions
};
Matter::Matter(int i, double k, char * filename)
:x(k>0? this->kap(x): this->kap(filename) ) {
// ... rest of initialisation
}
This is however not working:
test.cpp: In constructor 'Matter::Matter(int, double, char*)':
test.cpp:32:21: error: no match for call to '(opacity) (void*&)'
test.cpp:32:42: error: no match for call to '(opacity) (char*&)'
test.cpp:32:44: error: no matching function for call to 'opacity::opacity()'
test.cpp:32:44: note: candidates are:
test.cpp:20:1: note: opacity::opacity(char*)
test.cpp:20:1: note: candidate expects 1 argument, 0 provided
test.cpp:16:1: note: opacity::opacity(double)
test.cpp:16:1: note: candidate expects 1 argument, 0 provided
test.cpp:4:7: note: opacity::opacity(const opacity&)
test.cpp:4:7: note: candidate expects 1 argument, 0 provided
The first thing I had tried,
Mat开发者_如何学Goter::Matter(int i, double k, char * filename)
:kap(k>0? k: filename) { // use k<0 as a flag to read from filename
// ... rest of initialisation
}
also failed, because "the result of a ternary operator always has to be the same type" for compile-time reasons, as pointed out in a similar question (although they were not explained there, it seems).
Now, the inelegant solution would be to also overload the Matter
constructor based on the arguments that the kap
constructor should receive, but this is (1) very inelegant, especially since the Matter
constructor takes many variables and performs many actions (so a lot of code would be duplicated just to vary the kap
part of the constructor initialisation list), and (2) this can get out of hand if there is another class used with Matter
that also has different constructors: for M classes with N c'tors, one ends up with N^ M combinations...
Would someone have a suggestion or a work-around? Thanks in advance!
If opacity has a copy constructor, you could accomplish this in the initialization list, avoiding a default constructor, but at the cost of a copy:
Matter::Matter(int i, double k, char * filename)
:kap( ( 0 < k ) ? opacity(k) : opacity( filename ) ) { ... }
You will have to live with adding a default constructor to opacity
(which perhaps sets mode to 0
to indicate an invalid mode) and assign to kap
in the constructor body.
Matter::Matter(int i, double k, char * filename) {
if(k > 0)
kap = opacity(k);
else
kap = opacity(filename);
}
The parameter k
is a runtime value. It is not possible to make types and overloading result depend on runtime values.
To obviate copy overhead, and assuming you have a C++0x compiler, you could give opacity a move constructor and have a static function provide an instance of opacity
based on your logic and initialize your kap
member with the returned temporary opacity
.
You'd probably want to make kappa_array
some pointer like auto_ptr<double>
. Though if this data is used in a tight-loop, the savings from moveability may be dubious compared to the cost of locality and dereferencing the pointer.
opacity& get_opacity(double k, char * filename) {
if(k > 0)
return opacity(k);
else
return opacity(filename);
}
Matter::Mater(int i, double k, char * filename)
: kap(get_opacity(k, filename) {
//...
}
opacity::opacity(opacity&& other)
: mode(other.mode),
kap_const(other.kap_const),
kappa_array(std::move(kappa_array)) { }
Please don't test me on this, I'm pretty new with move semantics and rvalue references myself...
You can't use a ternary operator to select function overrides because the result of the operator only has a single type. In the case where the different branches have different types, they will be coerced to the result type or there will be a compile time error. See http://en.wikipedia.org/wiki/%3F:#Result_type
It couldn't really be any other way. Types need to be known by the compiler at compile time, but the result of the operation isn't known until run time.
精彩评论