0

I am wondering what was the best way using C++ to make a class constructor take an argument from a limited list. For example, if I have a class called Colour, then the constructor would only accept Red, Green, or Blue and store the value to a private variable. Then I would have a method called printColour which would print the variable.

Currently, I've been able to get this to work:

#include <iostream>
using namespace std;

class MyClass
{
public:
    typedef enum { A = 3, B = 7 } Options_t;
    MyClass(Options_t);
    void printVal(void);
private:
    Options_t _val;
};

MyClass::MyClass(Options_t val)
{
    _val = val;
}

void MyClass::printVal(void)
{
    cout << _val << endl;
}

MyClass hi(MyClass::A);

int main(void)
{
    hi.printVal();
    return 0;
}

However, the :: notation to access the class's struct seems a bit clunky (I want to write an Arduino Library so it might be beginners needing to use the constructor / methods.

Is there any better way to do this (maybe with . notation?) Thanks!

Edit: When I try to compile with MyClass x(2) (int instead of Options_t), I get this error:

class_enums.cpp:24:9: error: no matching constructor for initialization of 'MyClass'
MyClass hi(2);
        ^  ~
class_enums.cpp:4:7: note: candidate constructor (the implicit copy constructor) not viable: no known
      conversion from 'int' to 'const MyClass' for 1st argument
class MyClass
      ^
class_enums.cpp:14:10: note: candidate constructor not viable: no known conversion from 'int' to
      'MyClass::Options_t' for 1st argument
MyClass::MyClass(Options_t val)
         ^

So it seems that I can't just use an int as the argument?

ktstuff
  • 45
  • 1
  • 4
  • 2
    Using “::” is appropriate. However, how do you present me from doing “MyClass x(2)”? – Vlad Feinstein Apr 11 '21 at 14:48
  • C++ is a verbose language. Personally I believe that using the clunky notation keeps things simple and clear. – Raviteja Narra Apr 11 '21 at 16:46
  • @VladFeinstein the constructor only accepts an instance of the struct Options_t. Unless there's something I don't understand? – ktstuff Apr 12 '21 at 02:22
  • Look up the difference between `enum` and `enum class`. `enum` is the old way and will generally implicitly convert from numbers (so Vlad's snippet will work), whereas `enum class` is more strict in what it accepts. – Silvio Mayolo Apr 12 '21 at 02:26
  • If I try to compile with `MyClass x(2);`, I get these errors: `class_enums.cpp:25:9: error: no matching constructor for initialization of 'MyClass' MyClass x(2); ^ ~ class_enums.cpp:4:7: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'int' to 'const MyClass' for 1st argument class MyClass ^ class_enums.cpp:14:10: note: candidate constructor not viable: no known conversion from 'int' to 'MyClass::Options_t' for 1st argument MyClass::MyClass(Options_t val) ^ 1 error generated.` – ktstuff Apr 12 '21 at 02:32
  • It seems that it prevents me from using a number in the constructor? – ktstuff Apr 13 '21 at 01:26

2 Answers2

1

If you just want this during creation of hi, then you can do

MyClass hi(hi.A);

However, in your class, you must use the namespace accessor ::. The . is to access a member of an instance, not the actual class stuff.

They probably explained it better here: When do I use a dot, arrow, or double colon to refer to members of a class in C++? and here: What is the difference between "::" "." and "->" in c++

0

Thanks for the help! In the end I decided to go with this code (below) because according to this link, ints are not implicitly converted to an enum value.

Code:

#include <iostream>
using namespace std;

typedef enum { C = 1, D = 6 } Options;

class MyClass
{
public:
    MyClass(Options);
    void printVal(void);
private:
    Options _val;
};

MyClass::MyClass(Options val)
{
    _val = val;
}

void MyClass::printVal(void)
{
    cout << _val << endl;
}

MyClass hi(C);

int main(void)
{
    hi.printVal();
    return 0;
}

Also, doing it this way, I can define a struct for all the Options for the class, then just pass by reference to the constructor, like so:

#include <iostream>
using namespace std;

typedef enum { C = 1, D = 6 } Options;
typedef struct
{
    Options opt;
} MCConfig;

class MyClass
{
public:
    MyClass(Options);
    MyClass(MCConfig &);
    void printVal(void);
private:
    Options _val;
};

MyClass::MyClass(Options val)
{
    _val = val;
}

MyClass::MyClass(MCConfig & conf)
{
    _val = conf.opt;
}

void MyClass::printVal(void)
{
    cout << _val << endl;
}

MyClass hi(C);

int main(void)
{
    hi.printVal();
    MCConfig c;
    c.opt = D;
    MyClass m(c);
    m.printVal();
    return 0;
}
ktstuff
  • 45
  • 1
  • 4