C++ 将从一个函数返回的向量传递给另一个函数

C++ 将从一个函数返回的向量传递给另一个函数,c++,templates,gcc4.7,C++,Templates,Gcc4.7,我有一个返回字符串向量的函数 std::vector<std::string> getNames() { std::vector<std::string> names; names.push_back("one"); return names; } 我有另一个具有以下签名的构造函数的类,它实际上来自TCLAP: ValuesConstraint(std::vector<T>& allowed); 如果我尝试执行以下操作,我将收到一个错误 Value

我有一个返回字符串向量的函数

std::vector<std::string> getNames()
{
std::vector<std::string> names;
names.push_back("one");
return names;
}
我有另一个具有以下签名的构造函数的类,它实际上来自TCLAP:

ValuesConstraint(std::vector<T>& allowed);
如果我尝试执行以下操作,我将收到一个错误

ValuesConstraint<std::string> A( getNames() );
std::vector<std::string> v = getNames();
ValuesConstraint<std::string> A( v );
但当我做下面的事情时就不是了

ValuesConstraint<std::string> A( getNames() );
std::vector<std::string> v = getNames();
ValuesConstraint<std::string> A( v );
这发生在GCC 4.4.7中,但不在VS2010中。错误如下:

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> > >&)
为什么会发生此错误,我可以做些什么将返回值直接传递给构造函数

为什么会发生此错误,我可以做些什么将返回值直接传递给构造函数

ValuesConstraint的构造函数接受左值对非常量的左值引用只能绑定到左值,而不返回引用类型的函数调用表达式是右值

左值引用无法绑定到右值,因此无法将临时值传递给ValuesConstraint的构造函数。临时值是右值

如果您发现术语左值和右值混淆,您可以直观地将左值视为:

命名变量实际上是标识符;和/或 对象实际上是可以获取其地址的表达式 它们通常表示可以在程序中重复引用的对象,具有稳定的标识,从中隐式移动是不安全的,因为它们以后可以在程序中引用

相反,您可以将右值视为:

未命名的对象,如临时对象;和/或 文字,如42、false和3.14;和/或 表示具有不稳定标识的对象的表达式,如std::move的结果,即将从 它们通常表示无法在程序中重复引用的对象,或者在通过显式调用std::move重新分配它们之前承诺不再引用的对象,因此可以安全地从中移动

尽管如此,还是对以上内容持保留态度——这只是一个指导原则,你可以用它来建立一些直觉,了解什么是价值类别,以及如何判断某个表达式的价值类别。从形式上讲,事情有点复杂,上面的概括并不总是正确的

回到你的例子,在这种情况下:

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
您正在向构造函数传递一个左值,并且一切正常。另一方面,在这种情况下:

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!
您试图将右值传递给一个函数,该函数接受对non-const的左值引用,换句话说,它需要一个可修改的左值。这是违法的,编译器对此表示不满

这发生在GCC 4.4.7中,但不在VS2010中

这是因为VC10实现了一个不一致的编译器扩展,它允许将右值绑定到左值引用到非常量。但是要注意,这不是标准行为,如果你想编写可移植代码,你不应该依赖它。实际上,我建议你无论如何都不要依赖它

,将警告级别提高到4,以便从编译器获取警告并检测使用此非标准扩展的情况

为什么会发生此错误,我可以做些什么将返回值直接传递给构造函数

ValuesConstraint的构造函数接受左值对非常量的左值引用只能绑定到左值,而不返回引用类型的函数调用表达式是右值

左值引用无法绑定到右值,因此无法将临时值传递给ValuesConstraint的构造函数。临时值是右值

如果您发现术语左值和右值混淆,您可以直观地将左值视为:

命名变量实际上是标识符;和/或 对象实际上是可以获取其地址的表达式 它们通常表示可以在程序中重复引用的对象,具有稳定的标识,从中隐式移动是不安全的,因为它们以后可以在程序中引用

相反,您可以将右值视为:

未命名的对象,如临时对象;和/或 文字,如42、false和3.14;和/或 表示具有不稳定标识的对象的表达式,如std::move的结果,即将从 它们通常表示无法在程序中重复引用的对象,或者在通过显式调用std::move重新分配它们之前承诺不再引用的对象,因此可以安全地从中移动

尽管如此,还是对以上内容持保留态度——这只是一个指导原则,你可以用它来建立一些直觉,了解什么是价值类别,以及如何判断某个表达式的价值类别。从形式上讲,事情有点复杂,上面的概括并不总是正确的

回到你的例子,在这种情况下:

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
您正在向构造函数传递一个左值,并且一切正常。另一方面,在这种情况下:

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!
您试图将右值传递给接受对non-const的左值引用的函数,换句话说, 它需要一个可修改的左值。这是违法的,编译器对此表示不满

这发生在GCC 4.4.7中,但不在VS2010中

这是因为VC10实现了一个不一致的编译器扩展,它允许将右值绑定到左值引用到非常量。但是要注意,这不是标准行为,如果你想编写可移植代码,你不应该依赖它。实际上,我建议你无论如何都不要依赖它


,将警告级别提高到4,以便从编译器获取警告并检测使用此非标准扩展的情况。

但在VS2010中,这不是由于编译器扩展造成的。使用/W4编译,它应该警告它,或者/Za禁用扩展。但在VS2010中不是这样,这是由于编译器扩展。使用/W4编译,它应该警告它,或者/Za禁用扩展。在传递给构造函数之前将值赋给命名变量是最好的方法吗?或者getNames函数是否有一些潜在的修改?@denver:这不仅仅是最好的方法,如果你想编写可移植代码,这是唯一的方法。当然,除非您可以更改ValuesConstraint构造函数的签名,但我知道它不在您的控制范围内。value来自可以位于赋值左侧的值,而rvalue来自可以位于赋值右侧的值。请恕我直言:虽然这个词就是从这里来的,但它并不反映当前的用法。@Yakk:的确,这就是为什么我不想提及这个,以免混淆OP的原因。但是你是对的,这就是术语的来源。在传递给构造函数之前将值赋给命名变量是最好的方法吗?或者getNames函数是否有一些潜在的修改?@denver:这不仅仅是最好的方法,如果你想编写可移植代码,这是唯一的方法。当然,除非您可以更改ValuesConstraint构造函数的签名,但我知道它不在您的控制范围内。value来自可以位于赋值左侧的值,而rvalue来自可以位于赋值右侧的值。请恕我直言:虽然这个词就是从这里来的,但它并不反映当前的用法。@Yakk:的确,这就是为什么我不想提及这个,以免混淆OP的原因。但你是对的,这就是术语的来源。