【问题标题】:constexpr string literals in anonymous namespace?匿名命名空间中的 constexpr 字符串文字?
【发布时间】:2013-02-19 17:04:04
【问题描述】:

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

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

警告:‘ns::bar::type’有一个基数‘ns::base' 其类型使用匿名命名空间 [由 默认]

下面的工作示例代码:

// "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)

【问题讨论】:

  • #2 很容易回答:是的,它是安全的,只要您始终使用ns::bar::name 作为模板参数;另一个带有内容“bar”的单独字符串可能不在内存中的同一位置(并且肯定不会关闭优化,例如在调试构建期间),这将导致单独的 base&lt;&gt; 实例化(尽管是一个其行为方式相同)。
  • 所以我可能会使用#pragma 来禁用此特定实例化的警告,并且仍然感到温暖和快乐? (如果确实存在一个这样的编译指示 - 粗略的谷歌似乎无法找到它)
  • 不知道 ;-) 这个警告对我来说也是新的。我只回答了你问题的后半部分。你用的是什么编译器? (啊,没关系,我看到它是 gcc 4.7.2)。我无法使用 gcc 4.7.2 重现警告。而且我没有看到你提到的 anonymous 命名空间?
  • 嗯——考虑到在头文件中定义了内部链接的类型在包含头文件的每个文件中都是不同的类型,这样做真的会出错吗?也许#2毕竟不安全?
  • @Cameron - 您是否将代码拆分为 2 个单独的文件?上半场必须是头球。如果您将其全部编译到 1 个文件中,那么您不会收到警告 - 我猜是因为现在没有内部链接问题?

标签: c++ c++11 constexpr


【解决方案1】:

问题在于static constexpr char name[] = "bar"; 具有内部链接。

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

这很少是意图 - 因此是警告。

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

【讨论】:

  • +1,虽然吹毛求疵的是 type 是一样的,只是不是内存中的实例。
  • @Cameron 不真实。 base&lt;name&gt; 是不同“文件”中的不同类型。
  • @Johannes:我指的是name[],但你肯定说base&lt;name&gt; 在任何地方都是不同的类型。耶模板! :-)
【解决方案2】:

警告是因为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 的每个定义中,对应的名称[...] 可以引用一个常量对象 具有内部链接或无链接 [仅] 如果 [...] 使用对象的值(但不是地址)[...]

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

【讨论】:

  • 有没有办法强制name 具有外部链接 以我上面描述的方式使用它? IE; extern 之类的?
  • @lori 你可以声明它const char name[]; 并在单个源文件中定义它,例如main.cpp.
  • 你的意思是extern const char name[];
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-21
  • 1970-01-01
  • 2011-01-13
  • 2011-03-10
  • 1970-01-01
  • 2019-06-02
相关资源
最近更新 更多