下面的代码摘录是我正在努力理解的涉及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;
}
}
的模板参数有什么特别之处?
答案 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_type
(Z<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));
}