C++:友好模板参数的模板类型成员的正确语法?

C++: Correct syntax for friending a template type member of template parameter?

我有一个采用模板类型参数 (tTRAIT) 的 class。我想将 tTRAIT 的模板 type member alias 添加为朋友,但我无法弄清楚句法。 (这甚至可能吗?)。

template <bool bBOOL>
struct SFoo {};

struct STrait
    {
        template <bool bBOOL>
        using TFoo = SFoo<bBOOL>;
    };

template <typename tTRAIT>
struct SBar
    {
        template <bool bBOOL>
        friend typename tTRAIT::template TFoo<bBOOL>;
    };

SBar<STrait> bar;

Clang 的错误(在 friend 行)是:

error: friend type templates must use an elaborated type

我尝试了所有我能想到的可能组合:

friend tTRAIT::TFoo;
friend tTRAIT::template TFoo;
friend typename tTRAIT::TFoo;
friend typename tTRAIT::template TFoo;
template <bool bBOOL> friend tTRAIT::TFoo;
template <bool bBOOL> friend tTRAIT::TFoo<bBOOL>;
template <bool bBOOL> friend tTRAIT::template TFoo;
template <bool bBOOL> friend tTRAIT::template TFoo<bBOOL>;
template <bool bBOOL> friend typename tTRAIT::TFoo;
template <bool bBOOL> friend typename tTRAIT::TFoo<bBOOL>;
template <bool bBOOL> friend typename tTRAIT::template TFoo;
template <bool bBOOL> friend typename tTRAIT::template TFoo<bBOOL>;

我也尝试过使用 using,但似乎没有帮助。

作为一个丑陋的 hack(仅适用于 bool 参数),我可以通过手动为每个专业化添加好友来使其工作。

friend typename tTRAIT::template TFoo<false>;
friend typename tTRAIT::template TFoo<true >;

但这太恶心了。

有谁知道该怎么做,或者是否可以这样做?

我认为这是不可能的。来自标准草案 N4296:

§ 14.5.4/1 [temp.friend]

A friend of a class or class template can be a function template or class template, a specialization of a function template or class template, or a non-template function or class.

这不包括别名模板,因此该标准不支持您想要执行的操作。这可能是由于以下摘录(强调我的):

§ 14.5.7/1 [temp.alias]

A template-declaration in which the declaration is an alias-declaration (Clause 7) declares the identifier to be a alias template. An alias template is a name for a family of types.

别名模板命名一个单独的类型系列,因此即使有一些对此有意义的语法,您也会将别名模板而不是被别名的模板加为好友。

例如,GCC 会编译它(Clang 不会),但您实际上无法以任何合理的方式使用友谊:

template <bool B>
using MyTFoo = typename tTRAIT::template TFoo<B>;

template <bool> friend class MyTFoo; 

别名模板与别名模板不同的另一个例子:

template <template <typename...> class A, template <typename...> class B>
struct is_same_template : std::false_type{};

template <template <typename...> class A>
struct is_same_template<A,A> : std::true_type{};

template <typename T> using myvec = std::vector<T>;

//this fails
static_assert(is_same_template<myvec,std::vector>::value, "wat");

您使用显式实例化的手动交友将起作用,因为别名模板将折叠为与别名模板完全相同的类型。一个类似的例子:

//this passes!
static_assert(std::is_same<myvec<int>,std::vector<int>>::value, "wat");

我可以在 std=c++11 模式下使用 Clang 3.4.1 更进一步。

编译没有错误:

模板

struct SBar
    {
private:
    int j;
public:
        template <bool bBOOL>
        friend struct tTRAIT::TFoo;
        void setJ(int j) {
            this->j = j;
        }
};

但是...我收到此警告:警告:不支持朋友模板声明的依赖嵌套名称说明符 'tTRAIT::';忽略这个朋友声明 [-Wunsupported-friend] : friend struct tTRAIT::TFoo;

我可以确认 SFoo 类 不是朋友(private j 的原因...)

我可以全部编译并且 运行 的唯一方法是:

struct SBar
    {
private:
    int j;
public:
        template <bool bBOOL>
        friend struct sFoo;
        void setJ(int j) {
            this->j = j;
        }
};

很好,SFoo朋友,但它有点违反了OP要求([模板参数]的模板类型成员)...

我目前无法访问最新的 gcc,但我认为我们正处于编译器如何解释标准的边缘。我阅读了 TartanLlama 引用的章节,但无法确定这是否有意为之。也许我的第一次尝试会被 gcc 接受 ...