最近我第一次发现了这个错误:invalid initialization of reference of type ‘C*&’ from expression of type ‘B*’
。将代码从gcc 4.9
移植到gcc 6
时出现错误。下面我粘贴了一个最小的代码示例。
class A {
public:
A() : a(1) {};
virtual ~A() {};
int a;
};
class B : public A {
public:
B() : val(2) {};
int val;
};
class C : public A {
public:
C() : val(3) {};
float val;
};
int alloc_b(B*& entry) {
try {
entry = new B;
} catch(...) {
return -1;
}
return 0;
}
int alloc_c(C*& entry) {
try {
entry = new C;
} catch(...) {
return -1;
}
return 0;
}
template<typename T>
int whatever(const bool isB) {
T* entry = NULL;
if(isB) {
alloc_b(entry);
} else {
alloc_c(entry);
}
std::cout << entry->val << "\n";
}
int main() {
int rv;
B* ptrB;
C* ptrC;
whatever<B>(true);
whatever<C>(false);
return 0;
}
我理解错误的出现是因为在使用whatever
编译方法isB = true
时,它还尝试编译alloc_c()
调用,因此会检查T = B
和无法找到任何alloc_c(B*& entry)
方法,因此失败。使用类型invalid initialization of reference of type ‘B*&’ from expression of type ‘C*’
和whatever
调用方法C
时,另一种方式为isB = false
。
我只是想知道解决这个问题的最简洁方法是什么。我发现的唯一解决方案是创建一个带有一些特殊化的方法模板alloc
:
template<typename T>
int alloc(T*& entry) {
static_assert((std::is_same<decltype(entry), B>::value ||
std::is_same<decltype(entry), C>::value),
"Class must be A or B");
}
template<>
int alloc(B*& entry) {
return alloc_b(entry);
}
template<>
int alloc(C*& entry) {
return alloc_c(entry);
}
然后从whatever
函数内部,我调用alloc而不是其他alloc。
template<typename T>
int whatever(const bool isB) {
T* entry = NULL;
alloc(entry);
std::cout << entry->val << "\n";
}
但我确信必须有一种更清洁的方法来解决这个错误。
答案 0 :(得分:1)
我理解错误出现,因为在使用isB = true编译方法时,它也尝试编译alloc_c()调用,因此它会检查T = B而不能找到任何alloc_c(B *&amp; entry)方法,因此失败。
您可以通过提供此编译时评估isB
来阻止这种情况发生:
template<typename T, bool isB>
int whatever();
现在,使用if constexpr
将为您提供所需的功能,而不会过多地损害您的代码:
template<typename T, bool isB>
int whatever() {
T* entry = NULL;
if constexpr (isB) {
alloc_b(entry);
} else {
alloc_c(entry);
}
std::cout << entry->val << "\n";
}
whatever<B, true>();
whatever<C, false>();
修改强>
如果没有if constexpr
,SFINAE仍然有效 - 您只需要做更多的打字:
template<typename T, bool isB>
typename std::enable_if<isB, int>::type whatever() {
T* entry = NULL;
alloc_b(entry);
std::cout << entry->val << "\n";
}
template<typename T, bool isB>
typename std::enable_if<!isB, int>::type whatever() {
T* entry = NULL;
alloc_c(entry);
std::cout << entry->val << "\n";
}