I just switched my version of C++ to C++20 in visual studio and got a gazillion errors.
A lot of them are like this:
class Foo {
int value;
public:
Foo(int v) : value(v) {}
};
// A function that does something with a Foo
void bar(Foo& f)
{
// ...whatever
}
// This gives an error in C++20 but not in C++17
// The message is something like "cannot convert from Foo to T&"
bar(Foo(3));
What's the reason? Is there a trick to fixing this code? There's hundreds of them.
Addition: The code above is a bit useless because I was only thinking about the syntax when I typed it.
The real life cases in my code are things like wrapper classes:
eg.
class DataSource; // Abstract source of data
// Can read ASCII/UTF8/Unicode from a DataSource
class TextReader {
DataSource& source;
public:
TextReader(DataSource& s) : source(s) {}
};
// Read some text
void readText(TextReader& r)
{
}
void main()
{
FileDataSource d("file.text");
readText(TextSource(d)); // Neat!
}
The ability to pass references to temporaries can be a good thing so I don't see why they would remove it and break a lot of code in the process.
I just switched my version of C++ to C++20 in visual studio and got a gazillion errors.
A lot of them are like this:
class Foo {
int value;
public:
Foo(int v) : value(v) {}
};
// A function that does something with a Foo
void bar(Foo& f)
{
// ...whatever
}
// This gives an error in C++20 but not in C++17
// The message is something like "cannot convert from Foo to T&"
bar(Foo(3));
What's the reason? Is there a trick to fixing this code? There's hundreds of them.
Addition: The code above is a bit useless because I was only thinking about the syntax when I typed it.
The real life cases in my code are things like wrapper classes:
eg.
class DataSource; // Abstract source of data
// Can read ASCII/UTF8/Unicode from a DataSource
class TextReader {
DataSource& source;
public:
TextReader(DataSource& s) : source(s) {}
};
// Read some text
void readText(TextReader& r)
{
}
void main()
{
FileDataSource d("file.text");
readText(TextSource(d)); // Neat!
}
The ability to pass references to temporaries can be a good thing so I don't see why they would remove it and break a lot of code in the process.
Share Improve this question edited Mar 11 at 16:49 Chifti Saidi asked Mar 11 at 13:13 Chifti SaidiChifti Saidi 4825 silver badges10 bronze badges 9 | Show 4 more comments3 Answers
Reset to default 5What's the reason?
The reason is that C++ doesn't allow you to pass a temporary object as a non-const reference (this was true in C++17 and earlier versions of C++ as well, so you should have been receiving errors about this before C++20 as well)
As for the motivation behind why that isn't allowed, it's presumably because modifying an object that is going to be discarded as soon as the function call returns is usually a mistake (i.e. your changes would get thrown away), and so the C++ language specifiers wanted to be able to flag that mistake at compile-time rather than forcing the developer to debug it as a runtime error.
Is there a trick to fixing this code?
The usual fix is to change the type of the argument to a const-reference instead:
void bar(const Foo & f);
bar(Foo(3)); // works!
... but if you can't (or don't want to) do that, the other option is to construct the object separately so that it's no longer a temporary object:
void bar(Foo & f);
Foo foo;
bar(foo); // also works
There is no reason to keep it as behaviour never was conformant or corect. You need either one these or both:
void readText(const TextReader& r);
void readText(TextReader&& r);
Or a template which works as both:
template<class T> void readText( T&& r ) // enter your peferred way of constraint
You can't pass a temporary value to a non-const reference T&
.
If you can't use a constant reference const T&
because you need T
to be modifiable, then you can simply add an overload that passes by value:
void bar(Foo& f);
void bar(Foo f){
bar(f);
}
Then let copy elision do its thing.
bar
to accept aconst Foo &
? – wohlstad Commented Mar 11 at 13:15Foo
or aFoo&&
? – Bob__ Commented Mar 11 at 13:47/permissive
flag was the reason the code originally compiled: godbolt./z/M51d4oW4h – prapin Commented Mar 11 at 15:35