Reputation: 1206
I have a Value
class which I use for wrapping and converting basic types.
class Value {
public:
template <typename T>
Value(const T &value) : value_(StringMaker<T>(value)) {} // Not marked explicit
// Example -> Convert to String
std::string AsString() const;
// Example -> Convert to Int
int AsInt(bool *err_flag = nullptr);
private:
const std::string value_;
// Helper method to convert values to a string
template <typename T>
std::string StringMaker(const T &value);
};
I have other classes which use the Value
class e.g. the class Foo
:
struct Foo {
explicit Foo(const Value &value) : bar(value) {}
const Value bar;
};
With usage as follows:
// Normal use
std::cout << "Value converted to \"std::string\": " << Value(100).AsString()
<< std::endl;
// Normal use via Foo class
std::cout << "Value converted to \"std::string\" via \"Foo\": "
<< Foo(100).bar.AsString() << std::endl;
and output:
Value converted to "std::string": 100
Value converted to "std::string" via "Foo": 100
I would like to make use of a implicit constructor for Value
, but with cpplint and Cppcheck I get the warning:
Single-parameter constructors should be marked explicit. [runtime/explicit] [5]
If I add explicit
to the Value
constructor I get additional errors like:
error: no matching function for call to ‘Foo::Foo(int)’
<< Foo(100).bar.AsString() << std::endl;
Thus, is there a safe way to keep the static analysis tools happy and also use implicit conversion?
Here is a working example with additional use-cases.
Upvotes: 0
Views: 804
Reputation: 122830
Thus, is there a safe way to keep the static analysis tools happy [...]
Yes, make the converting constructor explicit
;)
You can only disable or ignore the warning or make the constructor explicit. Though the warning exists for a reason, implicit conversions can be a source of confusion. Because Value
s constructor is a template, you can pass any parameter to Foo
s constructor too. Latest when you want to add a second constructor for Foo
this can be confusing, for example:
struct Foo {
explicit Foo(const Value &value) : bar(value) { /* do something */}
explicit Foo(int x): bar(x) { /* do something else */}
const Value bar;
};
Then Foo(100)
suddenly calls a different constructor, which instead of something
does something else
. Hence I suggest to make contructor of Value
explicit.
Thus, I cannot do explicit with only Foo(100)
explicit
means just that: You have to explicitly call the constructor. Foo(100)
does not explicitly call Value
s constructor, but Foo(Value(100))
does. The sequence of conversions is exactly the same, but the code is more clear.
So instead of:
std::cout << Foo(100).bar.AsString() << std::endl;
write
std::cout << Foo(Value(100)).bar.AsString() << std::endl;
Alternatively, find a way to silence the warning. In rare cases implicit conversions are just right, but you need to be aware of the implications.
Upvotes: 2