复制省略在默认函数参数中有效吗?
Is copy elision valid in default function arguments?
考虑这段代码:
struct foo;
foo *p;
struct foo {
foo() { p = this; }
};
bool default_arg ( foo f = foo() )
{
return p == &f;
}
bool passed_in ( foo& f )
{
return p == &f;
}
int main()
{
std::cout << default_arg() << '\n';
foo f = foo();
std::cout << passed_in(f) << '\n';
}
我希望对于 default_arg
和 passed_in
的调用,f
将只是默认构造,因为副本将被删除*。这将导致对 return true
的两次调用。但是,Clang 3.7 nor GCC 5.3 都没有在 default_arg
.
的默认参数中省略副本
复制省略在默认参数中有效吗?也许我遗漏了一些关于如何在每次调用时评估默认参数的明显信息。
编辑: 重要的部分似乎是存在用户声明的复制构造函数。如果存在,则会发生复制省略。为什么这会有所作为?
*显然复制省略目前是可选的,但我希望 Clang 和 GCC 在可能的情况下这样做。
不幸的是我没有在草案中找到声明(还)但是在 cppreference.com:
Even when copy elision takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
我认为这里的问题是,默认的 copy/move 构造函数被优化掉了,因此编译器无法省略副本。但是,如果您实施其中之一,则会发生省略:
#include <iostream>
struct foo;
foo *p;
struct foo {
foo() { p = this; std::cout << "default ctor\n"; }
// define your own copy/move ctor
// which are doing nothing
foo(foo const& f) { std::cout << "copy ctor\n"; }
foo(foo&& f) { std::cout << "move ctor\n"; }
};
bool default_arg ( foo f = foo() )
{
return p == &f;
}
bool passed_in ( foo& f )
{
return p == &f;
}
int main()
{
std::cout << default_arg() << '\n';
foo f = foo();
std::cout << passed_in(f) << '\n';
}
参见 here。
C++11 的规则可以在标准的第 12.8(31) 章中找到。 4 种情况下允许复制省略。此处应适用的规则是:
when a temporary class object that has not been bound to a reference
would be copied/moved to a class object with the same cv-unqualified
type
但是我在一个Note(1.9(11))中找到了这句话:
subexpressions involved in evaluating default arguments are considered
to be created in the expression that calls the function, not the
expression that defines the default argument
所以当 foo()
在 main
中默认构造时,整个事情应该类似于
bool passed_in_by_value ( foo f )
{
return p == &f;
}
std::cout << passed_in_by_value(foo()) << '\n';
这也 returns 错误。也许编译器在将其分配给参数时不再将其视为临时值。
不是真正的答案,但可能是一个提示...
正如 T.C. 所指出的,将可简单复制的小类型传递给函数时,复制省略可能是不可能的。
这是因为某些 ABI(例如 System V ABI)的调用约定假定足够小的普通可复制类型将在寄存器中传递,而不是通过内存传递。例如,SysV 将在 INTEGER
参数 class 中对此类类型进行分类,而不是 MEMORY
class.
这意味着如果您将这样的参数传递给需要获取参数地址的函数,则需要将寄存器内容复制到堆栈上,以便有一个有效地址。因此,无法执行从右值参数到按值参数的复制,即使语言规则可能说它是可能的。
当然,在这种情况下,为了提高效率,复制省略是毫无用处的,但对于那些好奇的人来说,在此类平台上进行复制省略的一个简单方法是使 class 不平凡可复制。这方面的一个例子是使析构函数由用户提供:
struct foo {
foo() { p = this; }
~foo(){} //user-provided
};
这会导致在 Clang 3.7 和 GCC 5.3 上发生复制省略。
考虑这段代码:
struct foo;
foo *p;
struct foo {
foo() { p = this; }
};
bool default_arg ( foo f = foo() )
{
return p == &f;
}
bool passed_in ( foo& f )
{
return p == &f;
}
int main()
{
std::cout << default_arg() << '\n';
foo f = foo();
std::cout << passed_in(f) << '\n';
}
我希望对于 default_arg
和 passed_in
的调用,f
将只是默认构造,因为副本将被删除*。这将导致对 return true
的两次调用。但是,Clang 3.7 nor GCC 5.3 都没有在 default_arg
.
复制省略在默认参数中有效吗?也许我遗漏了一些关于如何在每次调用时评估默认参数的明显信息。
编辑: 重要的部分似乎是存在用户声明的复制构造函数。如果存在,则会发生复制省略。为什么这会有所作为?
*显然复制省略目前是可选的,但我希望 Clang 和 GCC 在可能的情况下这样做。
不幸的是我没有在草案中找到声明(还)但是在 cppreference.com:
Even when copy elision takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
我认为这里的问题是,默认的 copy/move 构造函数被优化掉了,因此编译器无法省略副本。但是,如果您实施其中之一,则会发生省略:
#include <iostream>
struct foo;
foo *p;
struct foo {
foo() { p = this; std::cout << "default ctor\n"; }
// define your own copy/move ctor
// which are doing nothing
foo(foo const& f) { std::cout << "copy ctor\n"; }
foo(foo&& f) { std::cout << "move ctor\n"; }
};
bool default_arg ( foo f = foo() )
{
return p == &f;
}
bool passed_in ( foo& f )
{
return p == &f;
}
int main()
{
std::cout << default_arg() << '\n';
foo f = foo();
std::cout << passed_in(f) << '\n';
}
参见 here。
C++11 的规则可以在标准的第 12.8(31) 章中找到。 4 种情况下允许复制省略。此处应适用的规则是:
when a temporary class object that has not been bound to a reference would be copied/moved to a class object with the same cv-unqualified type
但是我在一个Note(1.9(11))中找到了这句话:
subexpressions involved in evaluating default arguments are considered to be created in the expression that calls the function, not the expression that defines the default argument
所以当 foo()
在 main
中默认构造时,整个事情应该类似于
bool passed_in_by_value ( foo f )
{
return p == &f;
}
std::cout << passed_in_by_value(foo()) << '\n';
这也 returns 错误。也许编译器在将其分配给参数时不再将其视为临时值。
不是真正的答案,但可能是一个提示...
正如 T.C. 所指出的,将可简单复制的小类型传递给函数时,复制省略可能是不可能的。
这是因为某些 ABI(例如 System V ABI)的调用约定假定足够小的普通可复制类型将在寄存器中传递,而不是通过内存传递。例如,SysV 将在 INTEGER
参数 class 中对此类类型进行分类,而不是 MEMORY
class.
这意味着如果您将这样的参数传递给需要获取参数地址的函数,则需要将寄存器内容复制到堆栈上,以便有一个有效地址。因此,无法执行从右值参数到按值参数的复制,即使语言规则可能说它是可能的。
当然,在这种情况下,为了提高效率,复制省略是毫无用处的,但对于那些好奇的人来说,在此类平台上进行复制省略的一个简单方法是使 class 不平凡可复制。这方面的一个例子是使析构函数由用户提供:
struct foo {
foo() { p = this; }
~foo(){} //user-provided
};
这会导致在 Clang 3.7 和 GCC 5.3 上发生复制省略。