声明在未命名命名空间中向前声明的友元是否违反 ODR?

Hed*_*ede 6 c++ undefined-behavior language-lawyer unnamed-namespace

我一直在使用一个肮脏的技巧,我使用未命名的名称空间来为每个文件指定不同的行为(用于单元测试)。感觉它不应该被明确定义,但它适用于过去六年发布的每个主要编译器。

我首先在匿名名称空间中前向声明一堆类:

namespace {
class context_check;
class context_block;
class register_test;
} // namespace
Run Code Online (Sandbox Code Playgroud)

然后我声明一个类并创建这些类friend

struct test_case
{
    // ...
};

class context {
// ...
private:
    // TODO: this feels like ODR-violation...
    friend class context_check;
    friend class context_block;
    friend class register_test;

private:
    void add_test(test_case&& test)
    {
        // ...
    }

    // ...
};
Run Code Online (Sandbox Code Playgroud)

在每个 cpp 文件中,我都有以下声明(为了简单起见,我将它们复制并粘贴到此处,但实际上我在标头中声明了未命名命名空间的内容):

// A.cpp

namespace {
extern context file_context;

class register_test {
public:
    register_test(const char* test_name, test_case&& test)
    {
        file_context.add_test(std::move(test));
    }
};

} // namespace

register_test register_test_1( test_case{"test1"} );
Run Code Online (Sandbox Code Playgroud)
// B.cpp

namespace {
extern context file_context;

class register_test {
public:
    register_test(const char* test_name, test_case&& test)
    {
        file_context.add_test(std::move(test));
    }
};

} // namespace

register_test register_test_2( test_case{"test2"} );
Run Code Online (Sandbox Code Playgroud)

那么每个 TU 都有自己的 等定义register_testcontext_check这个定义明确吗?感觉应该是UB。。。

use*_*522 3

似乎违反了 ODR 要求的以下条件,如https://en.cppreference.com/w/cpp/language/definition中所述:

程序中可以有多个以下各项的定义:类类型、[...],只要满足以下所有条件:

  • [...]
  • 每个定义中的名称查找都会找到相同的实体(在重载解析之后),除了 [...]

没有任何异常是相关的,并且查找例如context_checkin friend class context_check;,它是 定义的一部分context,会找到一个内部链接实体,如果定义包含在多个翻译单元中,则该实体不能相同。

该标准的实际相应规则可以在例如C++20 后草案的[basic.def.odr]/13.9中找到。