【问题标题】:Declaring friend class template via wrapper通过包装器声明朋友类模板
【发布时间】:2026-01-22 04:00:01
【问题描述】:

我见过以下 C++11 之前的代码,用作声明类模板朋友的技巧(在 C++11 中可以简单地使用 friend T; 完成)

template <typename T>
struct Wrapper
{
    typedef T type;
};

template <typename T> 
class Foo
{
    friend class Wrapper<T>::type; // effectively makes T a friend
};

struct Test{};

int main()
{
    Foo<Test> foo;
}

代码在 g++ (4.9/5.1/6) 上编译良好,但在 clang++ (3.5/3.6/3.7) 下编译失败并出现错误

错误:详细类型指的是 typedef

朋友类 Wrapper::type;

上述代码是否符合标准,即有效与否?

【问题讨论】:

标签: c++ templates language-lawyer friend


【解决方案1】:

§7.1.6.3/2:

如果标识符解析为 typedef-namesimple-template-id 解析为别名模板特化,elaborated-type-specifier 格式不正确

【讨论】:

    【解决方案2】:

    它不合规。 [class.friend]/3中friend的语法规则为:

    未声明函数的友元声明应具有以下形式之一:

    friend elaborated-type-specifier ;
    friend simple-type-specifier ;
    friend typename-specifier ;
    

    class Wrapper&lt;T&gt;::type 不是这些说明符类型。它不是详细类型说明符,因为Wrapper&lt;T&gt;::type 不是标识符类名,显然也不是其他两个要么。您正在寻找的只是:

    friend typename Wrapper<T>::type;
    

    【讨论】:

    • friend class-key nested-name-specifier identifier ; 被 7.1.6.3 允许
    • @Columbo 因此,如果Wrapper&lt;T&gt;::type 是某种实际类型,而不是别名,您可以使用friend class Wrapper&lt;T&gt;::type?为什么允许类型而不允许别名?
    • 是的,这是有效的。不确定第二部分,但我会寻找 DR。
    【解决方案3】:

    [dcl.typedef]/p8:

    [ 注意:命名类类型或其 cv 限定版本的 typedef-name 也是 class-name (9.1)

    如果使用 typedef-name 来识别 elaborated-type-specifier (7.1.6.3) 的主题,则一个类定义 (第 9 条)、构造函数声明 (12.1) 或析构函数声明 (12.4),程序格式错误。 — 尾注 ] [示例

    struct S {
        S();
       ~S();
    };
    
    typedef struct S T;
    S a = T(); // OK
    struct T * p; // error
    

    结束示例 ]

    代码应该在模板实例化时失败,在 Clang 中它会正确地这样做。

    使用typename 代替struct 允许代码在两个编译器中传递。

    【讨论】:

    • 嗯,这是一个注释;不规范。