匿名命名空间中的constexpr字符串文字

constexpr string literals in anonymous namespace?

本文关键字:constexpr 字符串 文字 命名空间      更新时间:2023-10-16

我有下面的示例代码,它使用字符串文字作为模板参数,这样基类模板就可以访问字符串。

代码编译了,但我收到了一个我不完全理解的警告:

警告:"ns::bar::type"有一个基"ns::base<((const char*)(&ns::bar::name)>'其类型使用匿名命名空间[由启用默认值]

下面的工作示例代码:

// "test.h"
#pragma once
namespace ns 
{
    template <char const* str>
    struct base
    {
        const char *name() const { return str; }
    };
    namespace bar 
    {
        static constexpr char name[] = "bar";
        struct type : base<name> {};                // <-- this line here
    }
}
// main.cpp
#include <iostream>
#include "test.h"
int main()
{
    ns::bar::type f;
    std::cout << f.name() << std::endl;
    return 0;
}

所以我的问题是:

  1. 这个警告是什么意思
  2. 以我在这里所做的方式将字符串文本作为模板参数传递是否安全

(注意这与gcc 4.7.2有关)

问题与static constexpr char name[] = "bar";具有内部链接这一事实有关。

在标头中定义了内部链接的任何类型在包含标头的每个文件中都将是不同的类型

这很少是故意的,因此发出了警告。

在源文件中执行此操作时没有警告的原因是,该类型永远不能被多个文件引用,因此它将始终是一个类型。

警告是因为name在包含test.h的每个源文件中都有不同的地址。因为它有内部链接(static),所以每个翻译单元都会得到自己的副本;它们将不会被链接器统一。这意味着你的代码相当于:

template<int> struct base { ... };
static constexpr int val = some_value_different_in_every_source_file;
struct type: base<val> {};

您的代码是合法的,但如果您在另一个源文件中包含test.h,则它将违反一个定义规则:

3.2一个定义规则【basic.def.odr】

[…]
6-可以有不止一个类类型的定义[…]提供[…]:[…]

  • D的每个定义中,相应的名称[…]可以引用一个常量对象如果[…]使用了对象的值(但不是地址)[…],则具有内部链接或无链接[仅]

在类类型的定义中,您使用的是具有内部链接的对象的地址,因此在多个转换单元中使用它是未定义的行为。