我有这种类型:
\n\nstruct immobile {\n // other stuff omitted\n immobile(immobile&) = delete;\n immobile(immobile&&) = delete;\n};\nimmobile mk_immobile();\n// e.g. this compiles\n// mk_immobile() is a prvalue and i is its result object\nimmobile i(mk_immobile());\nRun Code Online (Sandbox Code Playgroud)\n\n我也有这个类模板:
\n\ntemplate<typename T>\nstruct container {\n std::variant<T, other_stuff> var;\n template<typename... Args>\n container(Args&&... args)\n : var(std::in_place_index<0>, std::forward<Args>(args)...) {}\n};\nRun Code Online (Sandbox Code Playgroud)\n\n我想container围绕 生成的对象构造mk_immobile(),该immobile对象用于初始化 的变体之一var。
container<immobile> c(mk_immobile());\nRun Code Online (Sandbox Code Playgroud)\n\n然而,这是行不通的。其一,std::variant的构造函数要求std::is_constructible_v<immobile, immobile>,但它不成立。更糟糕的是,即使这个简化版本也失败了:
template<typename T>\nstruct demonstration {\n T t;\n template<typename... Args>\n demonstration(Args&&... …Run Code Online (Sandbox Code Playgroud) 我接受了一次采访,得到了以下函数声明:
int f1(const std::vector<int> vec)
Run Code Online (Sandbox Code Playgroud)
我建议我们不要复制向量,而应该使用 const 引用(更不用说 const 复制没有多大意义),但面试官声称编译器复制省略会处理它。我当场想不出什么强有力的论据,但今天我做了一些研究。
我实现了以下两个简单的示例函数:
int f1(const std::vector<int> vec) {
const auto num = vec.size();
return num * num;
}
int f2(const std::vector<int>& vec) {
const auto num = vec.size();
return num * num;
}
Run Code Online (Sandbox Code Playgroud)
从godbolt汇编中可以清楚地看出,该f2函数有 2 条附加指令,因此它应该更慢。(我认为 2mov在现代 CPU 中技术上几乎是免费的)
我还使用quick-bench来测量这两个解决方案,但它证实了我的怀疑,通过const-ref更快。(即使对于 1 个元素)
我怀疑可能因为 的原因而不允许复制省略benchmark::DoNotOptimize(result);,但删除它后,我收到了类似的结果。
现在我有了这些结果,但我觉得还是不够有说服力。
你怎么认为?
对于使用其中一种而不是另一种,您有什么好的理由吗?
通常,这将被优化为不涉及复制大值(因为std::vector已启用移动语义):
std::vector<int> makeABigThing(){
std::vector<int> large_thing(1000, 0);
return large_thing;
}
Run Code Online (Sandbox Code Playgroud)
如果函数是虚方法,也可以以相同的方式优化:
struct Foo{
virtual std::vector<int> makeABigThing(){
std::vector<int> large_thing(1000, 0);
return large_thing;
}
};
Run Code Online (Sandbox Code Playgroud)
即,即使在运行时选择被调用函数,移动语义也能正常工作吗?
我注意到Visual Studio 2012中出现了一些非常奇怪的事情:定义一对像这样的对象:
auto objp = pair<int, LogMe>();
Run Code Online (Sandbox Code Playgroud)
会不会的Elid一对在VC11的复制/移动,这个调用会打印:
LogMe::LogMe - def.ctor!
LogMe::LogMe - move.ctor!
LogMe::~LogMe - dtor!
Run Code Online (Sandbox Code Playgroud)
也就是说,将创建一个临时对,然后将其移动到objp变量中.(声明pair<...> obj;它只记录默认的ctor)
我单独使用LogMe测试对象进行交叉检查:
cout << "# Construct Object via auto obj = ...\n";
auto obj = LogMe();
# Construct Object via auto obj = ...
LogMe::LogMe - def.ctor!
Run Code Online (Sandbox Code Playgroud)
这里的任务将被删除.
这似乎是VC11特有的,因为在IDEOne(使用gcc 4.8.1)中测试它表明,无关紧要的移动总是在那里被省略.
这里发生了什么? 不能依赖被删除的初始化副本让我感到紧张.
注意:发布与调试版本的测试显示相同的结果.(我原本期望,因为复制省略与MSVC中的优化标志无关.)
要测试的完整源代码(另请参阅ideone链接):
#include "stdafx.h"
#include <iostream>
#include <map>
using namespace std;
struct LogMe {
std::string …Run Code Online (Sandbox Code Playgroud) 它们是否跨不同的目标文件工作?它们是否跨不同的DLL工作?
我知道这取决于编译器.我很好奇是否有任何编译器和优化设置可以使这项工作.
我这里有两段代码给你看.它们是两个类,每个类都提供一个Move Constructor和一个返回临时函数的函数.
我很困惑:在这两种情况下,我都定义了一个Move Constructor和一个返回临时的随机成员函数.但行为改变了,我的问题就是原因.
请注意,在以下示例中,运算符<<被重载以便打印列表(在第一种情况下)和双数据成员(在第二种情况下).
移动构造者得到了认可
template<typename T>
class GList
{
public:
GList() : il{ nullptr } {}
GList(const T& val) : il{ new Link<T>{ val,nullptr } } {}
GList(const GList<T>& copy) {}
GList(GList<T>&& move)
{
std::cout << "[List] Move constructor called" << std::endl;
// ... code ...
}
// HERE IS THE FUNCTION WHICH RETURNS A TEMPORARY!
GList<T> Reverse()
{
GList<T> result;
if (result.il == nullptr)
return *this;
...
...
...
return …Run Code Online (Sandbox Code Playgroud) 请考虑以下示例:
#include <iostream>
#include <vector>
int main() {
std::vector<bool> vectorBool{false, true};
for(const auto &element : vectorBool) std::cout << std::boolalpha << element << ' ';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它会发出警告:
test.cpp:6:21: warning: loop variable 'element' is always a copy because the range of type 'std::vector<bool>' does not return a reference [-Wrange-loop-analysis]
for(const auto &element : vectorBool) std::cout << std::boolalpha << element << ' ';
^
test.cpp:6:9: note: use non-reference type 'std::_Bit_reference'
for(const auto &element : vectorBool) std::cout << std::boolalpha …Run Code Online (Sandbox Code Playgroud) 请考虑以下代码段:
template <typename>
struct dependent_false { static constexpr auto value = false; };
struct foo
{
foo() { }
template <typename T>
foo(const T&) { static_assert(dependent_false<T>::value, ""); }
};
struct proxy
{
operator foo() { return foo{}; }
};
int main()
{
(void) foo{proxy{}};
}
Run Code Online (Sandbox Code Playgroud)
编译时-std=c++17:
clang++ (trunk)成功编译代码;
g++(trunk)无法编译代码 - 它实例化foo(const T&).
编译时-std=c++11,两个编译器都拒绝代码.C++ 17中新的prvalue实现规则可能会影响此处的行为.
这里的正确行为是什么?
标准是否保证foo::foo(const T&)将(或不会)实例化?
标准是否保证隐式转换运算符优先于调用foo::foo(const T&),而不管它是否被实例化?
我一直被告知(并且一直在告诉)const,即使对于寿命很短的变量,保持- 正确性也是有价值和良好的做法,例如:
const std::string a = "Hello world";
Run Code Online (Sandbox Code Playgroud)
代替
std::string a = "Hello world";
Run Code Online (Sandbox Code Playgroud)
这个:
尽管自从现代C ++引入复制省略功能以来,标准中已有一些子句允许编译器调用move构造函数而不是复制构造函数:
在以下复制初始化上下文中,可以使用移动操作代替复制操作:
(3.1) 如果
return语句([stmt.return])中的表达式是一个(可能带有括号的)id表达式,该对象使用在最里面的封闭函数或lambda的主体或参数声明子句中声明的具有自动存储期限的对象进行命名-表达式,或(3.2) 如果
throw-expression[[expr.throw])的操作数是非易失性自动对象(函数或catch子句参数除外)的名称,其范围不会超出最内层封闭的结尾try-block(如果有一个),
这是否意味着使用const带有非默认复制/移动构造函数的对象实际上会产生性能损失,而不是在遇到这种省略的情况时会增加性能吗?