I read that it's a good practice to define single argument constructors explicit in order to avoid implicit conversions. I understand the pitfall of having int value promoted to class object. I wonder if it also applies to the constructors accepting reference types. How one can provoke implicit conversion in this case:
class Foo
{
public:
Foo(Bar& bar) { }
};
Does the situation changes if the constructor accepts pointers, is conversion from NULL and nullptr possible ?
class Foo
{
public:
Foo(Bar* bar) { }
};
Yes to both. A function with signature
void acceptFoo(const Foo& foo)
will make the compiler to create a Foo if you pass a Bar there.
Same for 0 and nullptr.
Related
I'm reading item 28 on smart pointers of Scott Meyers's More Effective C++ and have the following question.
A complete demonstration can be found at http://ideone.com/aKq6C0 .
A derived class pointer can be converted implicitly to a base class pointer:
class Base {};
class Derived : public Base {};
void foo(Base* b) { cout << "foo called on Base pointer" << endl;}
Derived *d = new Derived();
foo(d); //No problem
But such implicit conversion cannot happen for smart pointers, i.e. SmartPtr<Derived> cannot be implicitly converted to SmartPtr<Base>. So we use a member template for such conversions:
template<typename T>
class SmartPtr {
public:
//constructors, operator->, etc
//member template for type conversion
template<NewType>
operator SmartPtr<NewType> () {
return SmartPtr<NewType>(pointee);
}
private:
T* pointee;//the raw pointer
};
This can almost work, but it can cause ambiguity:
class Remote {};
class Base : public Remote {};
class Derived : public Base {};
void foo(const SmartPtr<Remote>& p) { cout << "remote" << endl;}
void foo(const SmartPtr<Base>& p) { cout << "base" << endl;}
SmartPtr<Derived> d(new Derived());
foo(d);//compile error: ambiguity
In this example, the compiler does not know whether it should convert d to SmartPtr<Base> or SmartPtr<Remote>, although for a raw pointer Base is apparently superior. The book says
The best we can do is to use member templates to generate conversion functions, then use casts in those cases where ambiguity results.
But how exactly do we apply cast here? foo(static_cast<SmartPtr<Base>>(d)) does not compile either. From the error message I can tell that the error comes from the use of non-const reference in the copy-constructor of SmartPtr. I'm wondering what's the correct way to make the function call.
//constructors
This is the most important part that you have omitted :)
Your cast is correct as such, it all depends on set of constructors you have. If you have one that takes non-const ref - you would get the error you mentioned, if you can just change ref to being const in copy ctor and code would compile.
It may be not very obvious, but when you call
return SmartPtr<NewType>(pointee);
you are constructing new SmartPtr<NewType> that have to accept T*, and NewType and T are different types. Most likely you only have ctor that accepts raw pointer of same type (i.e. T* for SmartPtr<T> and X* for SmartPtr<X>) so compiler is looking for another converting ctor to create your new SmartPtr and finds your copy ctor, but it can't bind new value to non const ref
Edit:
IF you are using c++11 adding move ctor would also solve your problem as it would be able to bind to rvalue
SmartPtr( SmartPtr<T>&& other): pointee(other.pointee) { other.pointee = nullptr; }
This question pertains to the preceding standard of C++11 (C++03). explicit prevents implicit conversions from one type to another. For example:
struct Foo
{
explicit Foo(int);
};
Foo f = 5; // will not compile
Foo b = Foo(5); // works
If we have a constructor that takes two or more parameters, what will explicit prevent? I understand that in C++11 you have braced initialization, so it will prevent constructions such as:
struct Foo
{
explicit Foo(int, int);
};
Foo f = {4, 2}; // error!
But in C++03 we don't have braced initialization, so what kind of construction is the explicit keyword preventing here?
It might be interesting if someone change the signature of your method with a default parameter :
struct Foo
{
explicit Foo(int, int = 0);
};
With the explicit keyword, you idiomatically say that you do not ever want a constructor to do implicit conversion.
If we have a constructor that takes two or more parameters, what will explicit prevent?
Nothing.
I have C++ code that boils down to something like the following:
class Foo{
bool bar;
bool baz;
Foo(const void*);
};
Foo::Foo(const void* ptr){
const struct my_struct* s = complex_method(ptr);
bar = calculate_bar(s);
baz = calculate_baz(s);
}
Semantically, the bar and baz member variables should be const, since they should not change after initialization. However, it seems that in order to make them so, I would need to initialize them in an initialization list rather than assign them. To be clear, I understand why I need to do this. The problem is, I can't seem to find any way to convert the code into an initialization list without doing one of the following undesirable things:
Call complex_method twice (would be bad for performance)
Add the pointer to the Foo class (would make the class size needlessly large)
Is there any way to make the variables const while avoiding these undesirable situations?
If you can afford a C++11 compiler, consider delegating constructors:
class Foo
{
// ...
bool const bar;
bool const baz;
Foo(void const*);
// ...
Foo(my_struct const* s); // Possibly private
};
Foo::Foo(void const* ptr)
: Foo{complex_method(ptr)}
{
}
// ...
Foo::Foo(my_struct const* s)
: bar{calculate_bar(s)}
, baz{calculate_baz(s)}
{
}
As a general advice, be careful declaring your data members as const, because this makes your class impossible to copy-assign and move-assign. If your class is supposed to be used with value semantics, those operations become desirable. If that's not the case, you can disregard this note.
One option is a C++11 delegating constructor, as discussed in other answers. The C++03-compatible method is to use a subobject:
class Foo{
struct subobject {
const bool bar;
const bool baz;
subobject(const struct my_struct* s)
: bar(calculate_bar(s))
, baz(calculate_baz(s))
{}
} subobject;
Foo(const void*);
};
Foo::Foo(const void* ptr)
: subobject(complex_method(ptr))
{}
You can make bar and baz const, or make the subobject const, or both.
If you make only subobject const, then you can calculate complex_method and assign to bar and baz within the constructor of subobject:
class Foo{
const struct subobject {
bool bar;
bool baz;
subobject(const void*);
} subobject;
Foo(const void*);
};
Foo::Foo(const void* ptr)
: subobject(ptr)
{}
Foo::subobject::subobject(const void* ptr){
const struct my_struct* s = complex_method(ptr);
bar = calculate_bar(s);
baz = calculate_baz(s);
}
The reason that you can't mutate const members within a constructor body is that a constructor body is treated just like any other member function body, for consistency. Note that you can move code from a constructor into a member function for refactoring, and the factored-out member function doesn't need any special treatment.
You may use delegate constructor in C++11:
class Foo{
public:
Foo(const void* ptr) : Foo(complex_method(ptr)) {}
private:
Foo(const my_struct* s) : bar(calculate_bar(s)), baz(calculate_baz(s)) {}
private:
const bool bar;
const bool baz;
};
If you don't want to use the newfangled delegating constructors (I still have to deal with compiler versions that don't know about them), and you don't want to change the layout of your class, you could opt for a solution that replaces the constructor with const void * argument by a static member function returning Foo, while having a private constructor that takes the output from complex_method as argument (that latter much like the delegating constructor examples). The static member function then does the necessary preliminary computation involving complex_method, and ends with return Foo(s);. This does require that the class have an accessible copy constructor, even though its call (in the return statement) can most probably be elided.
I found this code:
foo::foo(const foo & arg) :
impl_(new impl(*arg.impl_))
{};
As far as I understand this constructor of class foo takes another object of the class foo as the only argument. What is not clear to me is why do we use * in front of arg. As far as I know, when we pass arguments by reference, we should treat this arguments in the "body" of the function as normal variables (and not as addresses of the variables, i.e. we should not use *).
The . operator has higher precedence than the indirection (*) operator, so your code is parsed as
*(arg.impl_)
impl_ appears to be a pointer, because you initialize it with new. To invoke the copy constructor, you have to pass an object, not a pointer, so you need to dereference it beforehand.
This is the copy constructor, and it takes a const reference (not an "object") as its argument.
You haven't shown the class definition, but
*arg.impl_
doesn't mean dereference arg and then look for some member called impl_, that would look like one of:
(*arg).impl_
arg->impl_
instead it means dereference the pointer arg.impl_, ie:
*(arg.impl_)
this is invoking the equivalent copy constructor for whatever type impl_ is.
Sample:
struct Impl {
int i_;
Impl() : i_(0) {}
Impl(const Impl& other) : i_(other.i_) {}
};
struct Foo {
Impl *impl_;
// Foo::Foo calls Impl::Impl
Foo() : impl_(new Impl()) {}
// Foo::Foo(const Foo&) calls Impl::Impl(const Impl&)
Foo(const Foo& other) : impl_(new Impl(*other.impl_)) {}
};
NB. this looks like the pimpl (or Pointer to Implementation) idiom.
Because impl_ is a pointer to a impl, which takes a a reference as copy constructor parameter (as is usually the case).
I have a URes class which contains a single pointer to <T>, with indirection operators -> and * overloaded so I can use the instance as a pointer directly.
However I also want to be able to pass my URes instance to functions that normally take the pointer inside the URes instance.
How do I make it so that when my URes instance object is passed to a function it is automatically cast to the pointer it contains?
The same way that you create any outbound conversion: by declaring and defining an operator.
In this case, you want a member operator T*.
Example:
template <typename T>
struct Foo {
operator T*() {
return 0;
}
};
void bar(int* p) {}
int main() {
Foo<int> f;
bar(f);
}
However, I'd recommend avoiding this and implementing a member T* get() instead. It should be an explicit step for a calling scope to obtain a pointer from your object.
You can do that by providing a conversion operator from that class to the pointer-type:
class Foo {
public:
operator int() const { // enables implicit conversion from Foo to int
}
};
Note that such implicit conversion can introduce unexpected behavior.