C++ constexpr变量的唯一地址

C++ constexpr变量的唯一地址,c++,c++11,extern,memory-address,constexpr,C++,C++11,Extern,Memory Address,Constexpr,是否可以为constexpr变量分配一个唯一的地址,即为变量可用的所有转换单元分配相同的地址(通常通过标头)?考虑下面的例子: // foo.hh #include <iostream> constexpr int foo = 42; // a.cc #include "foo.hh" void a(void) { std::cout << "a: " << &foo << std::endl; } // b.cc #include "

是否可以为constexpr变量分配一个唯一的地址,即为变量可用的所有转换单元分配相同的地址(通常通过标头)?考虑下面的例子:

// foo.hh
#include <iostream>
constexpr int foo = 42;

// a.cc
#include "foo.hh"
void a(void) { std::cout << "a: " << &foo << std::endl; }

// b.cc
#include "foo.hh"
extern void a(void);
int main(int argc, char** argv) {
  a();
  std::cout << "b: " << &foo << std::endl;
}
//foo.hh
#包括
constexpr int foo=42;
//抄送
#包括“foo.hh”

void a(void){std::cout我认为constexpr更适合于返回值为常量的函数。您可以将常量变量绑定到constexpr函数的返回值,并将其公开在外部。例如:

// constexpr.h
#ifndef __CONSTEXPR_H
#define __CONSTEXPR_H

extern const int foo;

#endif // __CONSTEXPR_H

// constexpr.cpp
#include "constexpr.h"

constexpr int foo_expr()
{
    return 42;
}

const int foo = foo_expr();

// unit1.cpp
#include <iostream>
#include "constexpr.h"

void unit1_print_foo()
{
    std::cout << &foo << " = " << foo << std::endl;
}

// unit2.cpp
#include <iostream>
#include "constexpr.h"

void unit2_print_foo()
{
    std::cout << &foo << " = " << foo << std::endl;
}

// main.cpp
extern void unit1_print_foo();
extern void unit2_print_foo();

int main(int, char**)
{
    unit1_print_foo();
    unit2_print_foo();
}

但是,它通常足以使
foo_expr
函数本身在外部可见,调用方可以使用
foo_expr()
获取值,而不是将其视为变量。

如果需要获取constexpr变量的地址,请将其声明为静态成员变量。可以通过这种方式将其用作常量表达式(而不是使用返回const的函数)

foo.h:

#ifndef FOO_H
#define FOO_H

struct Foo {
  static constexpr int foo { 42 }; // declaration
};

#endif // FOO_H
foo.cpp:

#include "foo.hpp"

constexpr int Foo::foo; // definition
bar.cpp:

#include "foo.hpp"

const int* foo_addr() {
  return &Foo::foo;
}

int foo_val() {
  return Foo::foo;
}
main.cpp:

#include <iostream>
#include "foo.hpp"

extern const int* foo_addr();
extern int foo_val();

constexpr int arr[Foo::foo] {}; // foo used as constant expression

int main() {
  std::cout << foo_addr() << " = " << foo_val() << std::endl;
  std::cout << &Foo::foo << " = " << Foo::foo << std::endl;
}

C++17
inline
variables

这一了不起的C++17功能使我们能够:

  • 对于每个常量,只需使用一个内存地址即可
  • 将其存储为
    constexpr
  • 从一个标题开始,在一行中完成
main.cpp

#include <cassert>

#include "notmain.hpp"

int main() {
    // Both files see the same memory address.
    assert(&notmain_i == notmain_func());
    assert(notmain_i == 42);
}
notmain.cpp

#include "notmain.hpp"

const int* notmain_func() {
    return &notmain_i;
}
编译并运行:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main
编译并运行:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main
。 另见:

内联变量的C++标准

C++标准保证地址相同。 10.1.6“内联说明符”:

6具有外部链接的内联函数或变量在所有转换单元中应具有相同的地址

cppreference解释说,如果未给出
静态
,则它具有外部链接

内联变量实现

我们可以通过以下方式观察其实施情况:

nm main.o notmain.o
其中包括:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i
MANNM
说的是
u

“u”符号是唯一的全局符号。这是ELF符号绑定标准集的GNU扩展。对于此类符号,动态链接器将确保在整个过程中 只有一个符号使用此名称和类型


所以我们看到有一个专门的ELF扩展。

我相信你的
foo
有内部链接,所以你看到了两个独立的副本。解决问题的通常方法是在头中声明一个
extern const int foo
,并在一个翻译单元中实现为
const int foo=42;
n它显然不是一个常量表达式,因为
inta[foo]
需要在编译时解决,而不仅仅是在链接时。也许有另一种方法可以实现你想要做的。那么……你到底想用这个地址做什么呢?@NicolBolas:到目前为止,我一直在努力解决
constepr
的问题。我习惯于对
const全局变量以避免重复的内存分配,即使有人决定获取这样一个野兽的地址。现在使用
constexpr
这似乎不再可能了。因此,我实际上试图找出的是,是否有某种方法可以避免数据重复,即使我现在无法想象某些奇怪的代码决定获取addr这些东西到处都有。在这种情况下,通常不说“内存分配”,同样的,
4+4
根本不涉及内存分配。cppreference还解释说,“如果类本身具有外部链接,则命名空间范围内类的静态数据成员具有外部链接(即,不是未命名名称空间的成员)。“只要某个类
Foo
不是未命名名称空间的成员,则
内联
静态成员
Foo::bar
将具有外部链接,且上述情况适用。
nm main.o notmain.o
main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i