为什么要使用内联未命名命名空间?

28

对于专家来说很简单:C++11允许声明无名命名空间为inline。这对我来说似乎是多余的;在无名命名空间中声明的东西已经被视为在包围命名空间中声明。

那么我的问题是:这意味着什么?

inline namespace /*anonymous*/ {
    // stuff
}

它与传统方式有何不同

namespace /*anonymous*/ {
    // stuff
}

我们从C++98中了解并喜爱的特性,是否有人可以举出使用inline时不同行为的示例?

编辑:仅作澄清,因为这个问题已被标记为重复:我不是在问一般命名的内联命名空间。我理解它们的用例,而且我认为它们很棒。我具体地询问声明一个未命名命名空间为inline是什么意思。由于未命名命名空间必须始终局部于TU,因此符号版本控制似乎并不适用,因此我想知道添加inline实际上是做了什么。


此外,关于未命名命名空间,标准[7.3.1.1]说:

inline只有在unnamed-namespace-definition 中出现时才会出现

但对于我这个非语言律师来说,这似乎是一个同义反复的说法——“它只有在定义中出现时才会出现在定义中”!额外加分的话题,有谁能解释一下这个标准术语到底在说什么?

编辑: Cubbi 在评论中回答了额外加分的问题:

标准是说,未命名的命名空间定义会像它被X替换一样表现,其中inline出现在unnamed-namespace-definition中,当且仅当inline出现在X中。


可能是什么是内联命名空间?的重复问题。 - Shoe
1
@Jeffrey,这是关于未命名内联命名空间的问题。 - chris
1
@Jefffrey 那个问题是关于内联命名空间的一般性问题,我理解。这个问题具体询问了未命名命名空间被“内联”是什么意思,似乎没有被链接的答案(或任何其他我能找到的SO问题)涵盖到这一点。 - Tristan Brindle
1
奖励分很容易:标准规定未命名的命名空间定义的行为就像被X替换一样,其中inline出现在X中当且仅当它出现在未命名的命名空间定义中。 - Cubbi
@Cubbi 那实际上很有道理,如果我再仔细考虑一下,而不是只是读一读就“咦?”的话,也许就会有所领悟了。谢啦,你应该得到一个额外的奖励分数! :-) - Tristan Brindle
显示剩余2条评论
2个回答

17

我不知道在SO上回答自己的问题是否合适,但是经过一些尝试,我的好奇心得到了满足,所以我可以分享一下。

内联命名空间的定义不仅包括将名称提升到封闭命名空间中(对于未命名命名空间来说,这种情况总会发生),还允许在内联命名空间中定义的模板在其外进行特化。结果表明,这也适用于未命名的命名空间:

inline // comment this out to change behaviour
namespace { 
    template <typename T> struct A {};
}

template <> struct A<int> {};

没有inline会导致g++抱怨尝试从不同的名称空间特化模板(虽然Clang不会)。使用inline,它可以完美编译。在两个编译器中,任何在特化中定义的东西仍然被标记为具有内部链接(根据nm的结果),就像在未命名的名称空间中一样,但我猜这是可以预期的。我真的想不出来为什么这会有用,但这就是现实。

更有用的效果可能来自于关于内联名称空间(argument-dependent lookup)的更改,这也影响未命名的内联名称空间。考虑以下情况:

namespace NS {
    // Pretend this is defined in some header file
    template <typename T>
    void func(const T&) {}

    // Some type definition private to this TU
    inline namespace {
        struct A {};
    }

} // end namespace NS

int main()
{
    NS::A a;
    func(a);
}

没有 inline,ADL会失败,我们必须显式地写出 NS::func(a)。 当然,如果我们在顶层定义了未命名的命名空间(通常如此),那么无论是否内联,我们都不会得到ADL,但仍然......


8

我发现这里有一个用途:

namespace widgets { inline namespace {

void foo();

} } // namespaces

void widgets::foo()
{
}

在这个例子中,foo 具有内部链接,在使用 namespace::function 语法来确保函数签名正确后,我们可以稍后定义该函数。如果您不使用 widgets 命名空间,则会定义一个完全不同的函数 void foo()。您也不需要重新打开命名空间,从而节省了一层缩进。
如果 widgets 命名空间中已经有另一个名为 foo 的函数,则这将导致歧义,而不是令人讨厌的 ODR 违规。

我不确定我理解你在这里做什么。为什么内部的“inline namespace”是必要的? - templatetypedef
@templatetypedef 内部的 inline namespace 使得 foo 具有内部链接。外部的 namespace 则是为了让你可以使用 namespace::function 语法来定义它,确保你获得正确的签名。 - Simple
1
啊,这很有道理。你可能想要更新你的答案并明确指出这一点,因为我第一次没有注意到它。 - templatetypedef
很酷,我喜欢这个!但是Clang好像不喜欢:我得到了“命名空间'widgets'中的'foo'的离线定义与任何声明不匹配”的错误。虽然使用g++可以正常工作,所以可能只是Clang的一个bug。 - Tristan Brindle
2
@NirFriedman 如果没有 inline,你就无法在不再次打开未命名的命名空间的情况下定义 foo 函数。 - Simple
显示剩余2条评论

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