【发布时间】:2015-09-25 17:31:20
【问题描述】:
当今的许多 C++ 代码在最大程度上倾向于模板加载。它们是库:STL、Boost.Spirit、Boost.MPL 等等。他们鼓励用户以形式声明功能对象
struct S { /* presence of non-virtual member functions and operators, but absense of non-static data members or non-empty base classes */ }; S const s{};。它们中的大多数是无状态的(即static_assert(std::is_empty< S >{}); 持有)。对于那些使用 ODR 的对象,不管它们的空性如何,data 文件增长 1 个字节的部分(sizeof(S) == 1 用于空类型S,因为随后分配的对象的所有地址都应该不同)。即使在 Boost.Spirit 的简单语法中,也有很多这样的 ODR 使用的空类。但是给他们留空间是绝对没有意义的。
我尝试使用以下代码 (-Ofast) 在 coliru 上测试 clang:
#include <utility>
#include <type_traits>
#include <cstdlib>
#include <cassert>
template< std::size_t index >
struct S {};
namespace
{
template< std::size_t index >
S< index > value = {};
}
template< typename lhs, typename rhs >
std::ptrdiff_t
diff(lhs & l, rhs & r)
{
return (static_cast< char * >(static_cast< void * >(&r)) - static_cast< char * >(static_cast< void * >(&l)));
}
template< std::size_t base, std::size_t ...indices >
std::ptrdiff_t
bss_check(std::index_sequence< indices... >)
{
return (diff(value< (base + indices) >, value< (base + indices + 1) >) + ...);
}
template< std::size_t size, std::size_t base >
bool
enumerate()
{
return (bss_check< base >(std::make_index_sequence< size >{}) + 1 == size);
}
template< std::size_t size, std::size_t ...bases >
bool
expand(std::index_sequence< bases... >)
{
return (enumerate< size, (bases * size) >() && ...);
}
template< std::size_t size = 100, std::size_t count = size >
bool
check()
{
return expand< size >(std::make_index_sequence< count >{});
}
int
main()
{
static_assert(std::is_empty< S< 0 > >{});
assert((check< DIM >()));
return EXIT_SUCCESS;
}
并得到结果(DIM == 100 的 size 实用程序的输出,即 100 * 100 个类):
text data bss dec hex filename
112724 10612 4 123340 1e1cc ./a.out
如果我将diff(lhs & l, rhs & r) 的签名更改为diff(lhs l, rhs r) 以抑制使用ODR,那么结果是:
text data bss dec hex filename
69140 608 8 69756 1107c ./a.out
几乎等于(仅对data 部分感兴趣)assert((check< DIM >())); 行的简单注释的情况(text 部分的大部分是可预测的 DCE 优化出来的):
text data bss dec hex filename
1451 600 8 2059 80b ./a.out
因此我得出结论,对于使用 ODR 的空类没有优化。
对于明确指定的模板参数,可以使用简单的类型过滤器:
template< typename type >
using ref_or_value = std::conditional_t< std::is_empty< std::decay_t< type > >{}, std::decay_t< type >, type && >;
但在我看来,推导的模板类型没有简单的解决方法。
现代编译器中是否隐含上述优化?如果是,如何启用它?如果没有,目前是否有实现所需行为的技术?
我知道有时对象的地址会咕哝很多,但在上述情况下并非如此。
我认为变量或类型的属性(例如[[immaterial]])会很方便。也许这样的属性(用于类)应该拒绝获取属性类实例地址的可能性(编译时硬错误)或 address-of 运算符& 应该返回无意义的值(实现定义)。
【问题讨论】:
-
尝试创建诸如本地变量之类的对象,也许是临时变量。由于它们是无状态的,因此不应引入任何运行时成本。
-
@NeilKirk 我无法创建局部变量模板。
-
@NeilKirk 不管怎样,我试图发起一个关于一般问题的讨论,而不是我的本地问题。
-
这是几个讨论的重复,
//stackoverflow.com/questions/3849334/sizeof-empty-structure-is-0-in-c-and-1-in-c-why和//stackoverflow.com/questions/2362097/why-is-the-size-of-an-empty-class-in-c-not-zero和其他讨论,其中大部分已关闭,因为它出现了很多。基本上,C 不允许空结构,而在 C++ 中,这意味着T a[10];sizeof a / sizeof * a转换为除以零(以及许多其他相关含义)。从空类派生不会施加这样的惩罚。它在标准中,而不是优化选项 -
@JVene 这是完全不同的问题。至少尝试阅读标题。你完全不明白我在说什么吗?
标签: c++ c++11 optimization compiler-optimization one-definition-rule