通过包装器声明朋友 class 模板

Declaring friend class template via wrapper

我看过以下 C++11 之前的代码,用作声明 class 模板友元的技巧(在 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) 下失败并出现错误

error: elaborated type refers to a typedef

friend class Wrapper::type;

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

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

A friend declaration that does not declare a function shall have one of the following forms:

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

class Wrapper<T>::type 是这些说明符类型中的 none。它不是 elaborated-type-specifier 因为 Wrapper<T>::type 不是 identifierclass-name,显然也不是其他两个之一。您正在寻找的只是:

friend typename Wrapper<T>::type;

[dcl.typedef]/p8:

[ Note: A typedef-name that names a class type, or a cv-qualified version thereof, is also a class-name (9.1)

If a typedef-name is used to identify the subject of an elaborated-type-specifier (7.1.6.3), a class definition (Clause 9), a constructor declaration (12.1), or a destructor declaration (12.4), the program is ill-formed. — end note ] [Example:

struct S {
    S();
   ~S();
};

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

end example ]

代码应该在模板实例化时失败,而在 Clang 中它是正确的。

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

§7.1.6.3/2:

If the identifier resolves to a typedef-name or the simple-template-id resolves to an alias template specialization, the elaborated-type-specifier is ill-formed.