匿名命名空间中的constexpr字符串字面量?

9
我有以下示例代码,它使用字符串字面值作为模板参数,以便基类模板可以访问该字符串。
代码编译通过,但我收到了一个警告,我并不完全理解:
警告:“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时出现的警告)


问题#2很容易回答:只要您始终将ns::bar::name用作模板参数,则是安全的;另一个独立字符串具有内容“bar”,可能不会在内存中的同一位置(当然,在优化关闭时,例如在调试构建期间),这将导致单独的base<>实例化(虽然行为方式相同)。 - Cameron
那么我可以潜在地使用 #pragma 来禁用这个特定实例周围的警告,而仍然感到温暖和愉快吗?(如果确实存在这样的编译指示 - 似乎无法通过简单的谷歌搜索找到它) - Steve Lorimer
我也不知道;-) 这个警告对我来说也是新的。我只回答了你问题的后半部分。你使用的编译器是什么?(啊,算了,我看到它是gcc 4.7.2)。我无法在gcc 4.7.2中重现这个警告。而且我也没有看到你提到的anonymous命名空间? - Cameron
嗯——考虑到在头文件中定义具有内部链接的类型将在包含该头文件的每个文件中成为不同的类型,那么这样做实际上是否会出错?也许第二种方法并不安全? - Steve Lorimer
@Cameron - 你把代码分成两个文件了吗?第一部分必须在头文件中。如果你将所有内容编译到一个文件中,那么就不会收到警告 - 我猜是因为现在没有内部链接问题了? - Steve Lorimer
@lori:糟糕,这是我的错误!我把所有东西都放在一个文件里了。是的,你是对的——#2不应该是安全的,除非“name”只存在于一个翻译单元中(在分配的内存方面)。 - Cameron
2个回答

7
问题在于 static constexpr char name[] = "bar"; 具有内部链接。 任何在头文件中定义了内部链接的类型将在每个包含该头文件的文件中都是不同的类型。 这很少是意图 - 因此会出现警告。
之所以在源文件中执行此操作时没有警告,是因为该类型永远不可能被多个文件引用 - 因此它始终是一个类型。

4
+1,虽然要挑刺的是类型是相同的,只是内存中的实例不同。 - Cameron
1
@Cameron 不正确。base<name> 在不同的“文件”中是不同的类型。 - Johannes Schaub - litb
@ Johannes:我所指的是 name [],但你肯定是对的,base <name> 在任何地方都是不同的类型。模板万岁! :-) - Cameron

4
警告是因为在每个包含test.h的源文件中,name将具有不同的地址。由于它具有内部链接(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的定义中,对应的名称[...]只能引用一个具有内部或无链接性质的const对象[...],仅当[...]使用该对象的值(而非地址)时。

您在类类型的定义中使用了具有内部链接性质的对象的地址,因此在多个翻译单位中使用它将导致未定义的行为。


有没有办法强制name具有外部链接并以我上面描述的方式使用它?即; extern或类似的东西? - Steve Lorimer
@lori,你可以声明它为 const char name[]; 并在单个源文件中定义它,例如 main.cpp - ecatmur
1
你是指 extern const char name[]; 吗? - Steve Lorimer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接