Multiple explicit constructors and implicit conversion - c++

I have a class like this:
class Foo
{
private:
std::string m_data;
public:
Foo() = default;
explicit Foo(double value);
explicit Foo(float value);
explicit Foo(int64_t value);
explicit Foo(bool value);
explicit Foo(const std::string& value);
explicit Foo(const char* value);
};
... because I'd like to be able to construct Foo instances from integers, booleans, floats, doubles, and so on.
Let's say I have another class Bar, which has a method that takes a reference to const Foo:
void append(const Foo& foo);
Why does the compiler complain when I do the following?
instanceOfBar.append(3.5);
Reference to type Foo could not bind to an rvalue of type 'float'
instanceOfBar.append(4);
Reference to type Foo could not bind to an rvalue of type 'int'
instanceOfBar.append(true);
Reference to type Foo could not bind to an rvalue of type 'bool'
I am using GCC 12.2.1

Your constructors of the class Foo are declared with the function specifier explicit. So, you need to convert argument expressions explicitly to the type Foo to allow the function append() to bind them to the const lvalue reference.
As, for example:
instanceOfBar.append( Foo( 4 ) );
In fact, you are trying to do something like the following:
struct A { explicit A( int ) {} };
const A &a = 10;
This code will not compile because the compiler can not implicitly convert the integer constant 10 to the type A.
But, if you remove the function specifier explicit in the constructor declaration, then this code snippet will compile:
struct A { A( int ) {} };
const A &a = 10;

Related

Specifying a `const int&` that doesn't bind temporaries

Is there a way to specify a constant reference as an argument type (of a function) that doesn't bind temporaries?
That is, is there a standard way to specify both that the reference is const but that it can only bind to non-temporary values, with attempting to bind to a temporary resulting in a compiler error¹?
For example, in the case of the constructor of a class X,
class X
{
public:
X(const int &value)
: mValue(value)
{}
private:
const int &mValue;
};
what would be a nice way to ensure that
class Y
{
public:
/* ... */
X* GetXForValue() const
{
return new X(mValue);
}
private:
int mValue;
};
compiles, but when called with a temporary, e.g. X x(100);, it does not?
¹) I could overload the function for int&& and then not define it, but that would give the wrong idea and only result in a linker error.
A general solution is to add a deleted overload:
void foo(const int&);
void foo(const int&&) = delete;
The latter is a better match for rvalues, and thus the undesired program would select the deleted overload and become ill-formed.

Why does non-const method hide const overload?

Given the code below:
class A
{
public:
A(): value( 0 ) {}
int* get()
{
return &value;
}
const int& get() const
{
return value;
}
private:
int value;
};
int main()
{
A a;
const int& ref_value = a.get();
}
results in the following compilation error:
prog.cpp: In function 'int main()':
prog.cpp:23:35: error: invalid conversion from 'int*' to 'int'
const int& ref_value = a.get();
^
It seems that the overloaded get() method with const modifier does get ignored completely and the compiler sees only the non-const definition of it. It is somehow understandable since the a object is not constant. One solution would be to make the a object constant. Though there are other two solutions that makes the code compileable:
Change the signature of the const get() method by different name or other parameters added.
int* get();
const int& get_changed() const; <-- this gets called
Change the non-const get() method to return a reference instead pointer.
int& get(); <-- this gets called
const int& get() const;
though with
int* get();
const int& get() const;
we have a compiler error.
What puzzles me is the reason behind all of these behavior.
When you have both a const and non-const overload of the same function with the same parameters, which one gets called depends only on the constness of the object on which you're invoking the function. So invoking on a non-const a must call the non-const overload.
It's exactly the same situation as this:
void foo(int *p);
void foo(const int *p);
int main()
{
int i;
const int ci;
foo(&i); // Calls the first overload
foo(&ci); // Calls the second overload
}
A const-qualified function can be called on a non-const-qualified object, but that requires a "nonconst to const" conversion. If there's an overload which doesn't require such a conversion (is a better match), it will be preferred.
The reason is that the compiler can't do overload resolution based on a different return type.
Only the function signature is taken into account for overload resolution. And the function signature consists only of the function name, the parameters, and the cv qualifiers - not the return type.

Templated class constructor uses wrong overload when in struct

The test is as following:
class NotInit{};
NotInit NOT_INIT;
template<class T>
class Optional{
T value;
bool has_value;
public:
Optional() : value(), has_value(false){}
explicit Optional(NotInit):value(), has_value(false) {}
explicit Optional(T const & val):value(val), has_value(true){}
explicit Optional(Optional<T> const & other) {}
Optional& operator=(Optional<T> const & other){}
Optional& operator=(T const & other){}
};
enum X {
FIRST
};
struct Some {
Optional<X> member;
};
int main(int, char**){
Optional<X> const opt(NOT_INIT);
Some s = {NOT_INIT};
return 0;
}
Clang 3.4 complains:
../st.cpp:31:12: error: no viable conversion from 'NotInit' to 'Optional<X>'
Some s = {NOT_INIT};
^~~~~~~~
1 error generated.
Why does it complain for the struct initialization but ont for the varaiable? Why is it not choosing the constructor with the proper parameter?
What overload is missing so that i can use this to init the struct?
I cannot use boost and i'm not sure this error would not appear if using boost::optional.
You are marking your constructors explicit. Thus when you are trying to initialize your struct Some with a brace-initializer list, you are triggering an implicit conversion...
This is prevented here:
class NotInit;
template <typename T>
class Optional {
// Here => Optional(NotInit) cannot be called implicitly
explicit Optional(NotInit):value(), has_value(false) {}
};
/* ... */
Some s = {NOT_INIT}; // Implicit call to Optional<X>(NotInit): whoops!
If you remove the explicit, you can keep:
Some s = {NOT_INIT};
If you chose not to, then you'll have to instantiate s like this:
Some s = {Optional<X>(NOT_INIT)};
In any case, you'll have to remove the explicit keyword on your copy constructor (which has to perform a copy and not be a "dummy" constructor).
Why? Because once the Optional<X> object is built from NOT_INIT, a copy has to be made to s.member.
Note: You could also provide a move constructor, which would be more appropriate in that case, since the object you get from the conversion of NOT_INITis an rvalue.

C++ template member function having pointer reference template parameter

class BI {
public:
virtual void fun() = 0;
virtual ~BI() {}
};
class B : public BI {
public:
void fun() {}
};
template <typename T>
class A {
T* obj;
public:
void funT(const T*&) /* adding reference is creating error */;
};
template <typename T>
void A<T>::funT(const T*& obj) {
}
int main() {
A<B> obj;
obj.funT(new B());
}
when compiling the above code with g++ compiler, I am getting the error: no matching function for call to A::funT(B)*. But when I remove the reference '&' operator from the funT() declaration as void funT(const T* obj), then it compiles and works fine. Why the reference operator is not allowed here?
You're asking for a reference to a pointer, you can't get references (unless they're C++11 rvalue references) to temporary values.
Make sure you have a lvalue passed as parameter to have the reference working.
e.g.
#include <iostream>
using namespace std;
class BI {
public:
virtual void fun() = 0;
virtual ~BI() {}
};
class B : public BI {
public:
void fun() {}
};
template <typename T>
class A {
T* obj;
public:
void funT(const T*&);
};
template <typename T>
void A<T>::funT(const T*& obj) {
}
int main() {
A<B> obj;
const B* ptr = new B(); // <-- This is an lvalue
obj.funT(ptr);
delete ptr; // Also clean it up after you used it
}
http://ideone.com/T4QJzi
Here is a more simple program which exhibits the same problem:
void fun(const int*&) {}
int main() {
int x;
fun(&x);
}
It yields the following error:
invalid initialization of non-const reference of type ‘const int*&’
from an rvalue of type ‘int*’
That makes sense. fun takes an argument of type "reference to non-const pointer to const int", and we tried to pass it a temporary of type "pointer to int". References to non-const types don't bind to temporaries, because temporaries are usually immutable. In this case, if we were allowed to pass &x as the argument to fun, then fun would be able to modify the address of x, which doesn't make any sense.
As you noticed, removing the & makes the code well formed:
void fun(const int*) {}
Now we are simply passing a pointer to int where a value of type pointer to const int is expected, which is a simple implicit conversion.
Alternatively, you might have intended for fun to take an argument of type "reference to const pointer to int":
void fun(int* const&) {}
Or a reference to const pointer to const int:
void fun(const int* const&) {}
But a reference to a const pointer is kind of silly, as a simple pointer would be just as good.
Finally, you could keep your original declaration of fun, and just avoid trying to pass a temporary as its argument.
The error msg is clear. Parameter type is of reference to pointer to T, but you are sending pointer to T. Temporaries cannot be passed as reference in this case. You can write:
int main() {
A<B> obj;
const B* b=new B(); //create a lvalue
obj.funT(b);
delete b; // make sure to release memory.
}

prevent pass-by-ref of temporary object

I have a class that 'remembers' a reference to some object (e.g. an integer variable). I can't have it reference a value that's destructed immediately, and I'm looking for a way to protect the users of my class from doing so by accident.
Is an rvalue-reference overload a good way to prevent a temporary to be passed in?
struct HasRef {
int& a;
HasRef(int& a):a(a){}
void foo(){ a=1; }
};
int main(){
int x=5;
HasRef r1(x);
r1.foo(); // works like intended.
HasRef r2(x+4);
r2.foo(); // dereferences the temporary created by x+4
}
Would a private rvalue overload do?
struct HasRef {
int& a;
HasRef( int& a ):a(a){}
void foo(){ a=1; }
private:
HasRef( int&& a );
};
... HasRef r2(x+1); // doesn't compile => problem solved?
Are there any pitfalls I didn't see?
If you have to store a const reference to some instance of type B into your class A, then surely you want to be ensured, that lifetime of A instance will be exceeded by the lifetime of B instance:
B b{};
A a1{b}; // allowed
A a2{B{}}; // should be denied
B const f() { return B{}; } // const result type may make sense for user-defined types
A a3{f()}; // should also be denied!
To make it possible you should explicitly to = delete; all the constructor overloadings, which can accept rvalues (both const && and &&). For this to achieve you should just to = delete; only const && version of constructor.
struct B {};
struct A
{
B const & b;
A(B const & bb) : b(bb) { ; } // accepts only `B const &` and `B &`
A(B const &&) = delete; // prohibits both `B &&` and `B const &&`
};
This approach allows you to prohibit passing to the constructor all kinds of rvalues.
This also works for built-in scalars. For example, double const f() { return 0.01; }, though it cause a warning like:
warning: 'const' type qualifier on return type has no effect [-Wignored-qualifiers]
it still can has effect if you just = delete; only && version of constructor:
struct A
{
double const & eps;
A(double const & e) : eps(e) {} // binds to `double const &`, `double &` AND ! `double const &&`
A(double &&) = delete; // prohibit to binding only to `double &&`, but not to `double const &&`
};
double const get_eps() { return 0.01; }
A a{0.01}; // hard error
A a{get_eps()}; // no hard error, but it is wrong!
For non-conversion constructors (i.e. non-unary) there is an issue: you may have to provide = delete;-d versions for all the combinatorically possible versions of constructors as follows:
struct A
{
A(B const &, C const &) {}
A(B const &&, C const &&) = delete;
// and also!
A(B const &, C const &&) = delete;
A(B const &&, C const &) = delete;
};
to prohibit mixed-cases like:
B b{};
A a{b, C{}};
Ignoring the fact the code isn't valid and just answering the question about the private overload...
In C++11 I would prefer a deleted function to a private function. It's a bit more explicit that you really can't call it (not even if you're a member or friend of the class.)
N.B. if the deleted constructor is HasRef(int&&)=delete it will not be chosen here:
int i;
HasRef hr(std::forward<const int>(i));
With an argument of type const int&& the HasRef(const int&) constructor would be used, not the HasRef(int&&) one. In this case it would be OK, because i really is an lvalue, but in general that might not be the case, so this might be one of the very rare times when a const rvalue reference is useful:
HasRef(const int&&) = delete;
That shouldn't compile. A good C++ compiler (or really almost any C++ compiler that I've ever seen) will stop that from happening.
I'm guessing you're compiling in MSVS. In that case, turn off language extensions and you should get an error.
Otherwise, not that even marking the reference const extends the lifetime of the temporary until the constructor finishes. After that, you'll refer to an invalid object.