2

I have a function that returns a vector of strings

std::vector<std::string> getNames()
{
std::vector<std::string> names;
names.push_back("one");
return names;
}

I have another class with a constructor with the following signature (this is actually from TCLAP):

ValuesConstraint(std::vector<T>& allowed);

I receive an error if I attempt to do the following

ValuesConstraint<std::string> A( getNames() );

But not when I do the following

std::vector<std::string> v = getNames();
ValuesConstraint<std::string> A( v );

This occurs in GCC 4.4.7 but not in VS2010. The error is as follows:

error: no matching function for call to ‘TCLAP::ValuesConstraint<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::ValuesConstraint(std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >)’

note: candidates are: TCLAP::ValuesConstraint<T>::ValuesConstraint(std::vector<T, std::allocator<_CharT> >&) [with T = std::basic_string<char, std::char_traits<char>, std::allocator<char> >]

note:                 TCLAP::ValuesConstraint<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >::ValuesConstraint(const TCLAP::ValuesConstraint<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >&)

Why is this error occurring, and what can I do to pass the return value directly to the constructor?

denver
  • 2,863
  • 2
  • 31
  • 45
  • 2
    `but not in VS2010`, this is due to a compiler extension. Compile with `/W4` and it should warn about it or `/Za` to disable extensions. – Jesse Good Jul 08 '13 at 20:51

1 Answers1

4

Why is this error occurring, and what can I do to pass the return value directly to the constructor?

The constructor of ValuesConstraint<> accepts an lvalue (lvalue references to non-const can only bind to lvalues), while function call expressions that do not return a reference type are rvalues.

lvalue references cannot bind to rvalues, so you cannot pass a temporary to the constructor of ValuesConstraint<> (temporaries are rvalues).

If you find the terms lvalue and rvalue confusing, you can (intuitively) think of lvalues as:

  • named variables (actually, identifiers); and/or
  • objects (actually, expressions) you can take the address of

They usually denote objects that can be repeatably referenced in a program, with a stable identity, from which it is not safe to move implicitly (because they could be referenced later on in a program).

On the contrary, you can see rvalues as:

  • unnamed objects like temporaries; and/or
  • literals (like 42, false, and 3.14); and/or
  • expressions denoting objects with an "unstable" identity (like the result of std::move()), which are about to be moved from

They usually denote objects that cannot be repeatably referenced in a program (or objects you promised not to reference anymore before reassigning them by explicitly calling std::move()) and are, therefore, safe to move from.

Take the above with a grain of salt though - it is just a guideline you can use to build some intuition on what value categories are, and how to tell what is the value category of a certain expression. In formal terms, things are a bit more complicated and the above generalizations are not always correct.

So getting back to your example, in this situation:

std::vector<std::string> v = getNames();
//                       ^
//                       Named variable! Can take its address! lvalue!
ValuesConstraint<std::string> A( v );
//                               ^
//                               OK! The constructor accepts an lvalue
//                               reference to non-const, and I am giving
//                               it an lvalue

You are passing an lvalue to the constructor and everything works fine. In this situation, on the other hand:

ValuesConstraint<std::string> A( getNames() );
//                               ^^^^^^^^^^
//                               getNames() returns a vector by value, this
//                               means a temporary will be constructed as the
//                               return value of the function, and temporaries
//                               are unnamed... so, that's an rvalue!
//                               And the constructor accepts an lvalue reference
//                               to non-const! So this is an ERROR!

You are trying to pass an rvalue to a function that accepts an lvalue reference to non-const (in other words, it wants a modifiable lvalue). That's illegal, and the compiler complains about it.

This occurs in GCC 4.4.7 but not in VS2010

That is because VC10 implements a non-conforming compiler extension that allows binding rvalues to lvalue references to non-const. Watch out though, this is not standard behavior, and if you want to write portable code, you shouldn't rely on it (I'd suggest you not to rely on it anyway, actually).

As suggested by Jesse Good in the comments, raise the warning level to 4 in order to get a warning from the compiler and detect situations where you are making use of this non-standard extension.

Community
  • 1
  • 1
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • Is assigning the value to a named variable prior to passing to the constructor the best way to go? Or is there some potential modification to the getNames function? – denver Jul 08 '13 at 21:08
  • 1
    @denver: It is not simply the best way to go, it is the *only* way to go (if you want to write portable code). Unless, of course, you can change the signature of `ValuesConstraint`'s constructor, but I understood it's not under your control – Andy Prowl Jul 08 '13 at 21:09
  • `lvalue` comes from "value that can go on the left hand side of an assignment", and `rvalue` comes from "value that can go on the right hand side of an assignment". Take these origins with a grain of salt: while that is where the term came from, it does not reflect current usage. – Yakk - Adam Nevraumont Jul 08 '13 at 21:25
  • @Yakk: Indeed, which is why I preferred not to mention this (so the OP would not get confused). But you're right, that's where the terms come from. – Andy Prowl Jul 08 '13 at 21:26