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.
Related
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;
How do we code a must-non constant alias argument with its default argument must be constant as it's not being able to reference ?
and must be without overload function as its 91 lines long
Bar {
int on;
Bar(){};
Bar(bool o): off{o}{}
bool off;
}
int fooBar(int board, Bar& bar = static_cast<Bar>(1)) {
//...
}
gcc gave
error: cannot bind non-const lvalue reference of type 'Bar&' to an
rvalue of type 'Bar'
How to solve - with clear explanation - such this kind ?
The reason you get this error is, as you've discovered, that a non-const reference must bind to an lvalue: that is, it must bind to an object with a name. A temporary will not bind to a non-const reference: only to const Bar& or Bar&&, neither of which you want.
That leaves us a couple of options.
My personal favourite would be to not use default arguments at all, but instead provide an overload:
int fooBar(int board, Bar& bar) {
// ...
}
int fooBar(int board) {
// to save code duplication you can call in fooBar
Bar b{1};
return fooBar(board, b);
}
This is the easiest and most consistent option, in my opinion.
Other possibilities include:
making a global whose sole purpose is to be a default argument (yuck, and interferes with multithreading):
inline Bar default_bar;
int fooBar(int board, Bar& bar = default_bar) {
// ...
}
using a pointer instead, with the caveat that you must check if they've provided an argument or not:
int fooBar(int board, Bar* bar = nullptr) {
// ..., but make sure to test bar!
}
or otherwise using a std::optional<std::reference_wrapper<Bar>> or some-such type.
You can define such helper function:
template <typename Ty> Ty &make_ref(const Ty &cref)
{
return const_cast<Ty&>(cref);
}
And then use it as follows:
int fooBar(int board, Bar& bar = make_ref(Bar(1))) {
//...
}
I have an interesting predicament here where the code will compile if I pass a variable by value to the function but not if I pass it by reference, and I'm not sure why. In header.h:
#include <iostream>
#include <string>
void get_name(std::string &name)
{
getline(std::cin, name);
return;
}
template <class T, class U>
class readonly
{
friend U;
private:
T data;
T& operator=(const T& arg) {data = arg; return data;}
public:
operator const T&() const {return data;}
};
class myClass
{
private:
typedef readonly<std::string, myClass> RO_string;
public:
RO_string y;
void f()
{
get_name(y); // compile error
}
};
The main.cpp implementation file simply includes this header file, creates an instance of myClass and then calls f(). When doing so, it won't compile properly. The problem lies in the fact that I'm passing the variable y by reference to the get_name function. If I change the function so that I pass by value instead, everything compiles properly and works as expected (except I'm obviously not making changes to y anymore). However, I don't understand this behavior. Why does this happen and is there an optimal way to fix it in this situation?
Your conversion operator:
operator const T&() const {return data;}
Only allows implicit conversion to a const reference. This makes sense since you are interested in making something read only. But get_name requires a non-const reference to a string. That is, get_name expects to be able to mutate its input. The type being passed in cannot convert to a mutable string reference, only to a constant string reference, so it doesn't compile.
When you pass by value, it simply constructs a new string from the const reference to pass into the function, which is fine.
Intuitively, a function called get_name should probably take a const reference since it shouldn't need to mutate its input just to get the name.
As explained by Nir Friedman, get_name(y); invoques the implicit conversion operator which gives you a const reference.
But, as you wrote friend U;, myClass has access to the private member data of y and allows you to do:
get_name(y.data);
Scott Meyers in this talk at 44:15, says const Rvalue references are used in c++0x standard library to capture certain overloads which are not supposed to be compilable.
Code snippet to illustrate the above mentioned point would be helpful. Thanks.
One usage that I found useful is to disable temporaries biding to reference members. For example, consider the code below:
struct Foo{};
class X
{
const Foo& _foo;
public:
X(const Foo&&) = delete; // prevents rvalue binding
X(const Foo& foo): _foo(foo){} // lvalue is OK
};
Foo get_Foo()
{
return {};
}
const Foo get_const_Foo()
{
return {};
}
Foo& get_lvalue_Foo()
{
static Foo foo;
return foo;
}
int main()
{
// X x1{get_Foo()}; // does not compile, use of deleted function
// X x2{get_const_Foo()}; // does not compile, use of deleted function
X x3{get_lvalue_Foo()}; // OK
}
You definitely want to disable a rvalue being passed to the constructor of X, since rvalues do not bind to const references via constructor parameters, so you end up with a dangling reference. Why const Foo&& and not simply Foo&&? Because if you use X(Foo&&) = delete;, then if your get_Foo() returns const Foo (which is a bad idea, but nevertheless is seen in actual code), it will bind to X(const Foo&) instead, and you end up with a dangling reference. However, X(const Foo&&) in the code above is a better match for a const Foo rvalue, so we obtain the desired effect of not being able to construct an X with a rvalue.
You may also ask why not defining X(Foo&) instead for the lvalue constructor. Then you won't be able to bind const lvalues. So the best approach is to mark X(const Foo&&) = delete;. Hope this clarifies it.
How can one invoke the second (overloaded) function?
(This example is present in Savitch's C++ textbook.)
(1) int& f(); // will be used in any l-value invocation
(2) const int& f() const; // will be used in any r-value invocation
I thought the first one is invoked in (a) and the second one in (b). But it is not.
(a) f() = 123; // the first one is invoked.
(b) f() + 3; // the first one is also invoked.
Only member functions can be const. So let's assume f actually is a member function in the textbook.
When the compiler has a choice between a const and a non const member function it will only use the const one if it has to. This is when the object the function is called on is const.
class A {
public:
int &f();
const int& f() const;
};
void func()
{
A a;
a.f(); // calls non const version
const A ca;
ca.f(); // call const version
}
(1) int& f(); // will be used in any l-value invocation
(2) const int& f() const; // will be used in any r-value invocation
These comments are wrong. If they appear in a textbook then I would recommend getting a different textbook.
Version 2 will be used when the expression denoting the object has const type, otherwise version 1 is used. It is nothing at all to do with lvalues and rvalues. Using Eelke's class definition:
A().f(); // invokes (1) on an rvalue
A const a;
a.f(); // invokes (2) on an lvalue
In your examples you don't actually show whether you are working on a const instance or not, but judging by your results, both must have been on a non-const one.
Overloading works on function parameters not by return type.
double f();
int f();
These are not overloading.
But these are:
double f();
int f(int);