11

I have base object called IList. Then I have VectorList, that inherits IList.

then I have function like this:

std::unique_ptr<IList> factory(){
    auto vlist = std::make_unique<VectorList>();
    return vlist;
}

This compiles without problem under gcc, but clang gives following error:

test_file.cc:26:9: error: no viable conversion from 'unique_ptr<VectorList, default_delete<VectorList>>' to
      'unique_ptr<IList, default_delete<IList>>'
        return vlist;

How is proper way to handle this kind of errors?

Nick
  • 8,479
  • 2
  • 36
  • 64
  • 4
    Could you provide an [mcve](http://stackoverflow.com/help/mcve)? – Chris Drew Sep 23 '15 at 14:50
  • I assume that `VectorList` is derived from the base class `IList` (judging by the names)? – Cory Kramer Sep 23 '15 at 14:52
  • 2
    @CoryKramer The question explicitly states that. "I have base object called `IList`. Then I have `VectorList`, that inherits `IList`." – James Adkison Sep 23 '15 at 14:52
  • yes, I used java word "inherits", sorry for that – Nick Sep 23 '15 at 14:53
  • If your classes are set up as you describe, [they will have no such issue](http://cpp.sh/46y6) – Cory Kramer Sep 23 '15 at 14:55
  • @CoryKramer Your example code is not 100% identical to this one. There's a difference between returning an rvalue and an lvalue, *especially* for a move-only type. – Angew is no longer proud of SO Sep 23 '15 at 14:55
  • 1
    @Angew I agree, but you [shouldn't have to explicitly `std::move` during a return](https://stackoverflow.com/questions/4316727/returning-unique-ptr-from-functions) – Cory Kramer Sep 23 '15 at 14:56
  • 3
    Don't forget the virtual destructor for this to work correctly with `unique_ptr`. – Rostislav Sep 23 '15 at 14:57
  • code is identical, classes are present (in includes). code compiles under gcc. there is virtual destructor in IList. Code compiles If I do it the "old" way, e.g. create class with new, add it to uniq_ptr of function return type etc. – Nick Sep 23 '15 at 14:59
  • 1
    I'd say this is a clang bug since it works when NOT using `auto`, see [this live example](http://melpon.org/wandbox/permlink/BES2pVxe8hsN6Awa) – m.s. Sep 23 '15 at 14:59
  • @Rostislav Isn't it more of an issue of deleting via the base pointer versus an issue specific to usage of `std::unique_ptr`? – James Adkison Sep 23 '15 at 14:59
  • 3
    @CoryKramer The point here is that the type of `vlist` is *not* the same as the return type. `vlist` is `unique_ptr`, the return type is `unique_ptr`. So that clause of the standard ("try a move first") does not apply. – Angew is no longer proud of SO Sep 23 '15 at 15:00
  • 1
    @m.s. But you've given it a *different* type than what `auto` does. Try it again with `std::unique_ptr`. – Angew is no longer proud of SO Sep 23 '15 at 15:00
  • @Angew you are right! so clang not applying copy elision seems to be fine in this case and GCC is not complying to the standard then? – m.s. Sep 23 '15 at 15:02
  • @JamesAdkison `shared_ptr` can handle this without a virtual destructor, so I thought it was worth mentioning. – Rostislav Sep 23 '15 at 15:02

1 Answers1

17

It appears (your version of) Clang is still following C++11 behaviour in this regard. In C++11, you had to use std::move in this case, because the type of vlist is different from the return type, and so the clause of "when returning an lvalue, try it as an rvalue first" did not apply.

In C++14, this restriction of "same types required" was lifted, and so in C++14, you shouldn't need the std::move in the return statement. But if you need your code to compile with your current toolchain, simply add it there:

return std::move(vlist);

The exact C++11 wording was this:

12.8/32 When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. ...

The criteria for copy elision (which include "same type") have to be met; they're just slightly extended to cover parameters as well.

In C++14 (N4140), the wording is broader:

12.8/32 When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

(Emphasis mine)

As you can see, copy elision criteria are no longer required for the return case.

Angew is no longer proud of SO
  • 156,801
  • 13
  • 318
  • 412
  • @Barry I've added a quote from N4140. They didn't change copy elision requirements, but relaxed those on `return` statements. – Angew is no longer proud of SO Sep 23 '15 at 15:09
  • 1
    then [this](http://melpon.org/wandbox/permlink/N5nHpyWXdvRlhMkO) should not compile using `g++ -std=c++11`, right? – m.s. Sep 23 '15 at 15:14
  • 1
    @Barry I believe that the "or" has weaker binding than the "and", i.e. I read it as "When the criteria for copy elision are met.... or when the *expression* in..." – Columbo Sep 23 '15 at 15:31
  • 4
    @Columbo Oh so (Criteria Met And Not Exception And Lvalue) Or (Return Statement Names Object)? This is why we don't write 82-word sentences, boys and girls. – Barry Sep 23 '15 at 15:34
  • @m.s. no it does not compile under c++11, also make unique is not present under c++11 – Nick Sep 23 '15 at 16:24
  • I can confirm that the code is compiled without warning under clang. – Nick Sep 24 '15 at 06:49
  • This is a DR against C++11 and so applies retroactively. See [CWG 1579](http://wg21.link/cwg1579). – T.C. Sep 24 '15 at 10:23
  • @T.C. Can you explain what "applies retroactively" means? Does a C++11 compiler have to implement the C++14 wording to be a conforming C++11 compiler? – Angew is no longer proud of SO Sep 24 '15 at 10:26
  • 2
    @Angew Well, nobody is selling a "conforming C++11" certification for compilers. It basically means that you can expect to see compilers implement the C++14 behavior even in their C++11 modes. – T.C. Sep 24 '15 at 10:41
  • @T.C. Makes sense, especially since the C++14 behaviour is strictly superior to the C++11 one. Thanks for clarifying. – Angew is no longer proud of SO Sep 24 '15 at 13:14
  • @Columbo OMG, 7 months later I make the [exact same mistake about this wording](http://stackoverflow.com/revisions/36752874/1)... SMH... I should write a proposal solely to address the grammar here. – Barry Apr 20 '16 at 20:27
  • @Barry I reckon you can get this fixed editorially. See the draft repo. – Columbo Apr 20 '16 at 21:00
  • @Columbo Threw out an [issue](https://github.com/cplusplus/draft/issues/712) with the first wording I thought of. Wonder what'll happen. – Barry Apr 20 '16 at 21:37