首页 > 解决方案 > 为模板类专业化的构造函数/析构函数授予友谊 - 在 C++17 下工作,但在 C++20 下失败

问题描述

我发现了一种情况,代码在 C++17 下编译成功,但在 C++20 下编译失败。
这阻止了我们将现有的代码设计升级到更新的标准。
为什么这不能在 C++20 下编译?这似乎是对向后兼容性的奇怪破坏。

当我尝试将友谊授予模板类专业化的构造函数/析构函数时,会发生错误。

我正在使用带有 Ninja 的 MSVC 进行编译。
在 C++17 下编译成功。
在 C++20 下,我收到以下错误:

error C2838: '{ctor}': illegal qualified name in member declaration
error C2838: '{dtor}': illegal qualified name in member declaration

这是在 C++20 下导致错误的代码的简化复制,但在 C++17 下编译成功:

template<typename T, int V> class B {};

// specialization of class B
template<typename T> class B<T, 0> {
private:
    T _t;   // private data member
public:
    constexpr inline B(T* p, int i) noexcept;   // B constructor declaration
    virtual inline ~B() noexcept;   // B destructor declaration
};

// empty class with only private static data, no methods
class A {
private:
    static int x;   // private static variable
public:
    // ERRORS HERE IN C++20, but compiles successfully in C++17
    template<typename T> friend B<T, 0>::B(T*, int) noexcept;   // C++20 ERROR
    template<typename T> friend B<T, 0>::~B() noexcept;   // C++20 ERROR
};

int A::x = 0;   // global definition of private static variable

template<typename T>   // B constructor definition
constexpr inline B<T, 0>::B(T* p, int i) noexcept : _t(0) { A::x++; }

template<typename T> // B destructor definition
inline B<T, 0>::~B() noexcept { A::x++; }

int main() {
    A a;
    B<const int, 0> b(0, 0);
}

B我知道我可以通过为整个班级模板(包括所有专业)授予友谊来解决这个问题,但这是不可取的,因为我们希望将友谊限制在尽可能小的限制。
只有一两个模板专精(几十个)实际上需要这种友谊,如果没有必要,我们不想将友谊授予其他模板专精。

标签: c++language-lawyerc++20friendfriend-function

解决方案


Apple clang 12.0.0 即使在 C++17 中也不允许这样做(但显然它在 GCC 10 下编译):

[timr@Tims-Pro:~/src]$ g++ --std=c++17 -c x.cpp
x.cpp:19:42: warning: dependent nested name specifier 'B<T, 0>::' for friend class declaration is not supported; turning off
      access control for 'A' [-Wunsupported-friend]
    template<typename T> friend B<T, 0>::B(T*, int) noexcept;   // C++20 ERROR
                                ~~~~~~~~~^
x.cpp:20:42: error: expected the class name after '~' to name the enclosing class
    template<typename T> friend B<T, 0>::~B() noexcept;   // C++20 ERROR
                                         ^

推荐阅读