最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - How to use a constructed object as a parameter in C++20? - Stack Overflow

programmeradmin0浏览0评论

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
  • Can you change bar to accept a const Foo & ? – wohlstad Commented Mar 11 at 13:15
  • 4 "This gives an error in C++20 but not in C++17" - It should. Are you using MSVC? When you set MSVC to C++20 mode, it implicitly disables the old non-conforming behavior. – Sebastian Redl Commented Mar 11 at 13:18
  • 2 Does that "function that does something with a Foo" also do something to a Foo? Could it accept a Foo or a Foo&&? – Bob__ Commented Mar 11 at 13:47
  • 5 This has been ill-formed for the entire lifetime of C++ (maybe there was a brief period in the 1980s when references were initially introduced that this worked). Nothing changed in C++20 (or 17, 14, or 11). – Barry Commented Mar 11 at 14:24
  • 2 Demo that /permissive flag was the reason the code originally compiled: godbolt./z/M51d4oW4h – prapin Commented Mar 11 at 15:35
 |  Show 4 more comments

3 Answers 3

Reset to default 5

What'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.

发布评论

评论列表(0)

  1. 暂无评论