17

I have to write constructor with two default parameters.

func(int arg1 , char* arg2 = "arg2", int arg3 = 1) //example

I am provided the scenario where the constructor is called and a value is given to arg1 and arg2 and arg3 is expected to use a default value. Then another object is instantiated and a value is given to arg1 and arg3, and default value for arg2 is expected to be used. Now the problem is, you "can't skip" default parameters is what I'm reading from the text and online. It's saying to order the default paramters from its likliness of being overloaded, but the scenario has one default parameter used while the other isn't. The hints for this question tells me to reorder the parameters/arguments. However, no amount of reordering that I've done seem to be able to resolve this issue.

Also, overloaded constructors can not be used. This has to be done by one constructor.

So how would one do this? I'm stumped and going a bit crazy over this :(

Mysticial
  • 464,885
  • 45
  • 335
  • 332
user1034846
  • 233
  • 1
  • 2
  • 6
  • 6
    You can't do this in C++ with a single function definition. Given that any other solution violates the bizarre constraints you have, what you want is impossible. Perhaps you can use a different language. – CB Bailey Nov 24 '11 at 22:07
  • Could you not use a `vector` of `object's? This way you could pass what you wanted and sort what wasn't passed based on `null` entries. – MoonKnight Nov 24 '11 at 22:20
  • Are you allowed additional constructs? e.g. [like this](http://stackoverflow.com/questions/8178187/default-argument-in-c/8178197#8178197) – Vlad Nov 24 '11 at 22:26
  • possible duplicate of [How can I cleanly specify which arguments I am passing?](http://stackoverflow.com/questions/8178392/how-can-i-cleanly-specify-which-arguments-i-am-passing) – Pubby Nov 24 '11 at 22:32
  • Can you use additional functions (not constructors)? – Chad Nov 25 '11 at 00:04

4 Answers4

13

Also, overloaded constructors can not be used. This has to be done by one constructor.

The only reason I can think of for this requirement is that the optional arguments have the same type. In that case, you're stuck and you'll want to look into the named constructor and/or named parameter idioms.

Otherwise, just define the extra constructor. This may involve some duplication wrt. the default values.

Foo(int arg1 , char const *arg2 = "arg2", int arg3 = 1)
{
    construct(arg1, arg2, arg3);
}

Foo(int arg1, int arg3)
{
    construct(arg1, "arg2", arg3);
}
tschale
  • 975
  • 1
  • 18
  • 34
Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • 3
    Those are some really nice idioms, thanks for sharing the links. One of the awesome things about Stack Overflow is that you can randomly peruse threads and learn some cool things :) +1 – Chris Parton Nov 25 '11 at 01:42
3

Peculiar restriction, that there must be only one constructor. Here's the closest I can think of:

#include <iostream>

// cheap and cheerful Boost.Variant
struct StringOrInt {
    char *s;
    int i;
    bool is_string;
    StringOrInt(char *s) : s(s), i(0), is_string(true) {}
    StringOrInt(int i) : s(0), i(i), is_string(false) {}
    bool isInt() { return !is_string; }
    int asInt() { return i; }
    char *asString() { return s; }
};

struct Foo {
    int m1;
    char *m2;
    int m3;
    Foo(int arg1, StringOrInt arg2 = "arg2", int arg3 = 1) : m1(arg1) {
        if (arg2.isInt()) {
            arg3 = arg2.asInt();
            arg2 = "arg2";
        }
        m2 = arg2.asString();
        m3 = arg3;
    }
    void print() {
        std::cout << m1 << " " << m2 << " " << m3 << "\n";
    }
};

int main() {
    Foo(1, "HelloWorld").print();
    Foo(1, 2).print();
}

Note that with GCC this generates warnings, since the conversion from a string literal to non-const char* is deprecated and unwise. But it's what you asked for, and fixing it so that the char* parameters and data member are const char* is easy enough.

A significant weakness is that this doesn't stop you writing Foo(1,2,3). To check that at compile-time I think you need multiple constructors. To check it at runtime, you could make the third parameter into another class, DefaultOrInt, where Default is a type used just for this purpose, supporting only one value used as the default value of arg3. Then if arg2.isInt() is true, check arg3.isInt() is false and if not throw logic_error.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
2

If you're allowed to pass an empty C-string when you've got no value for the second parameter, you could use a helper functor that'll check arg2 and return default value if it's empty. Something like this:

#define DEFAULT_ARG "arg2"

struct helper_class {
    char* operator()(char* arg)
    {
        if (*arg) return arg; else return DEFAULT_ARG;
    }
} helper;

class func {
    public:
    func(int arg1 , char* arg2 = "arg2", int arg3 = 1) {}
};

int main()
{
    func f1(42, helper(""), 9001);   // default 2nd argument
    func f2(42, helper("Its over 9000!"));
}

Not pretty, I know...

jrok
  • 54,456
  • 9
  • 109
  • 141
1

Right now you can use std::bind to do this kind of operation or in c++14/17 you can use lambda function and accomplish the same.