模板类成员使用类本身作为模板参数时发生未定义的类错误

时间:2019-08-26 22:12:32

标签: c++ templates typedef

下面的代码摘录是我正在努力理解的涉及C ++模板的问题的最小工作示例。照原样,代码可以很好地编译。如果在main函数外部被注释掉的行又被注释掉,我将收到与第73行有关的以下编译错误,即C<A> c(A<C<A>>(3.0));

testing.cpp(61): error C2079: 'C<A>::b_' uses undefined class 'A<C<A>>'
testing.cpp(10): note: see reference to class template instantiation 'C<A>' being compiled
testing.cpp(73): note: see reference to class template instantiation 'A<C<A>>' being compiled

第69行,即C<A> c(A<C<A>::this_class>(3.0));在所有情况下都进行编译。

#include <iostream>
#include <vector>

using namespace std;

template<class Z>
class A {

public:
    // typedef typename Z::traits_type Traits;

    A(double data = 2.0) : data_(data) { 
        cout << "Constructing A with data: " << data_ << endl;
    }

    double data() const {
        return data_;
    }

    void setup(Z* z) {
        z_ = z;
    }

    void doStuff() const {
        // cout << "A's foo: " << Traits::foo() << endl;
        cout << "Instance of Z's data_: " << z_->data_ << endl;
    }

private:
    double data_;
    Z* z_;

};

//struct CTraits {
//    static int foo() {
//        return 1;
//    }
//};

template<template <class> class B = A>
class C {

public:
    typedef C<B> this_class;
    // typedef CTraits traits_type;

    C(const B<this_class>& b = B<this_class>()) : data_(4.0), b_(b) { 
        cout << "Constructing C using B with data: " << b_.data() << endl;
        b_.setup(this);
    }

    void doStuff() const {
        b_.doStuff();
    }

private:
    double data_;

    friend class B<this_class>;
    B<this_class> b_;

};

int main(int argc, char* argv[]) {

    // The following line compiles regardless of whether all lines above that 
    // are commented are commented in or out.
    // C<A> c(A<C<A>::this_class>(3.0));

    // This will not compile if the lines above, outside of main, that are commented 
    // out are commented back in.
    C<A> c(A<C<A>>(3.0));

    return 0;
}

我的问题是,为什么C<A> c(A<C<A>>(3.0));以外的行main被注释掉,为什么C<A> c(A<C<A>::this_class>(3.0));行会导致编译错误?换句话说,什么更改导致编译失败?

此外,为什么C<A> c(A<C<A>>(3.0));不编译时,行C<A>::this_class会编译?换句话说,与仅使用A相比,使用C<A>作为private async Task<string> AuthenticationCertCallback(string authority, string resource, string scope) { try { var clientAssertionCertPfx = CertificateHelper.FindCertificateByThumbprint(_options.KeyVaultOptions.CertThumb); var assertionCert = new ClientAssertionCertificate(_options.KeyVaultOptions.Id, clientAssertionCertPfx); var context = new AuthenticationContext(authority, TokenCache.DefaultShared); var token = await context.AcquireTokenAsync(resource, assertionCert); return token.AccessToken; } catch (Exception ex) { Log.Error(ex, "Failed to acquire the certificate"); return string.Empty; } } 的模板参数有什么特别之处?

1 个答案:

答案 0 :(得分:2)

我设法使用clang++重现了您的问题。

让我们从一个简单的例子开始。保留类定义,但使用以下main

int main(int argc, char* argv[]) {
    A<C<A>> x(3.0);
}

这无法编译---让我们找出原因(非正式地)。

编译器必须实例化类A<C<A>>。为此,它将C<A>插入A作为模板参数Z。然后,它看到行typedef typename Z::traits_type Traits,它要求它实例化Z,即C<A>。实例化C<A>时,它将模板参数B设置为A,因此当看到B<this_class> b_时,它必须实例化A<C<A>>。但这是我们一开始试图实例化的同一类!这就是为什么编译器会给出错误-它有一个“不完整的类型”,因为编译器意识到它已经开始实例化该类型,但是还没有完成。

这与定义以下错误消息的原因相同:

class D {
    D x;
};

现在是您问题的第二部分:为什么使用this_class可以解决问题? 要理解这一点,请考虑以下更简单的示例(再次具有与以前相同的类定义):

int main(int argc, char* argv[]) {
    typedef C<A>::traits_type y;
    A<C<A>> x(3.0);
}

这里发生的是typedef语句要求编译器尽早实例化C<A>。这样,它像以前一样对b_变量进行加密,并尝试实例化A<C<A>>,该变量再次将C<A>作为模板参数A插入Z中。然后,它看到了行typedef typename Z::traits_type Traits,但是此时它已经为typedef CTraits traits_type评估了C<A>,因此继续进行而没有尝试再次实例化C<A>

原因

总结上面的讨论,您所看到的行为有两个原因。首先,即使您只需要C<A>::traits_type,编译器也会尝试实例化整个C<A>。其次,在访问C<A>::traits_typeZ<A>::traits_type)时,编译器可以使用不完整的类型,但在A<C<A>> b_B<this_type> b_)中实例化类型本身时则不能。

C<A> c(A<C<A>::this_class>(3.0));起作用的原因是,C<A>::this_class迫使编译器尽早实例化C<A>

解决方法

一种可能的解决方法是提前显式实例化所需的模板,以防止发生循环:

template class C<A>;
int main(int argc, char* argv[]) {
    C<A> c(A<C<A>>(3.0));
}