c ++解决方法:`从类型'B *'

时间:2018-06-13 14:14:32

标签: c++ templates

最近我第一次发现了这个错误: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";
}

但我确信必须有一种更清洁的方法来解决这个错误。

1 个答案:

答案 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>();

Live Demo

修改

如果没有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";
}

Tested with gcc 6.1.0.