Ошибка компиляции с std :: is_same и оператором || - PullRequest
2 голосов
/ 25 апреля 2019

Я не понимаю, почему следующий код компилируется с Clang ++, но не с g ++.

#include <memory>

class A {
public:
    virtual ~A() {}
};
class B : public A {
public:
    virtual ~B() {}
};

template <typename Base, typename T>
inline bool isInstanceOf(const T& object) {
    // This line compiles with clang++ (7.0.1) and with gcc (8.3.1)
    // return std::is_same<Base, T>::value ? true : (dynamic_cast<const Base*>(&object) != nullptr);

    // This line compiles only with clang++
    return std::is_same<Base, T>::value || dynamic_cast<const Base*>(&object) != nullptr;
}

int main() {
    isInstanceOf<A>(B());
    isInstanceOf<A>(A());   // Compilation fails

    return 0;
}

Ошибка компиляции:

$> g++ -o bin -Wall -Werror test.cpp
test.cpp: In instantiation of 'bool isInstanceOf(const T&) [with Base = A; T = A]':
test.cpp:24:24:   required from here
test.cpp:19:79: error: the compiler can assume that the address of 'object' will never be NULL [-Werror=address]
  std::is_same<Base, T>::value || dynamic_cast<const Base*>(&object) != nullptr;
                                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~

cc1plus: all warnings being treated as errors

Почему создание шаблона не пропускает вторую часть теста?

Следующий код также компилируется:

if (std::is_same<Base, T>::value) {
    return true;
} else {
    return dynamic_cast<const Base*>(&object) != nullptr;
}

Сбой компиляции следующего кода:

if (std::is_same<Base, T>::value) {
    return true;
}

return dynamic_cast<const Base*>(&object) != nullptr;

Ответы [ 4 ]

0 голосов
/ 29 апреля 2019

Я думаю, что решение c ++ 17 также работает

if constexpr (std::is_same<Base, T>::value) {
  return true;
} else {
  return dynamic_cast<const Base*>(&object) != nullptr;
}
0 голосов
/ 26 апреля 2019

Этот код не выдает ошибку, но предупреждение. На мой взгляд, здесь полезно. Предупреждения - это то, что вы можете игнорировать, но в этом случае важно сделать следующее замечание: dynamic_cast тип в его базовый класс никогда не вернет NULL, и, поскольку это ссылочный тип, компилятор ожидает его не является указателем NULL, когда вы берете его адрес.

Этот код:

if (std::is_same<Base, T>::value) {
    return true;
} else {
    return dynamic_cast<const Base*>(&object) != nullptr;
}

... компилируется, потому что теперь вы представляете ситуацию, когда dynamic_cast действительно может вернуть вам NULL, так как приведение к Base может оказаться невозможным. Это верно, что G ++ не предупредил вас здесь.

Для меня это ситуация, когда GCC выдает вам более полные предупреждения, чем Clang, а не ситуация, когда GCC недостаточно, сообщая вам о коде, который не будет выполняться при определенных экземплярах шаблона.

0 голосов
/ 29 апреля 2019

Следующий код также компилируется без предупреждений:

template <typename Base, typename T>
bool isInstanceOf(const T& object) {
    if (!std::is_same<Base, T>::value) {
        return dynamic_cast<const Base*>(&object) != nullptr;
    }
}

Или вот этот:

template <typename Base, typename T>
inline bool isInstanceOf(const T* pointer) {
    return dynamic_cast<const Base*>(pointer) != nullptr;
}

template <typename Base, typename T>
inline bool isInstanceOf(const T& object) {
    return std::is_same<Base, T>::value || isInstanceOf<Base>(&object);
}

@ cyberbisson: вы, вероятно, правы, это должно быть отсутствие анализа кода, выполненного GCC.

Я не уверен, каков наилучший синтаксис для решения моей проблемы (или более читаемый / понятный). Опция «if / else» вызывает проблему с clang-tidy ( readability-else-after-return )

В настоящее время я выбрал вариант «троичный оператор», но мне нравится «две функции» один.

0 голосов
/ 26 апреля 2019

(это не «истинный» ответ, но использование ответа более практично, чем использование замечания)

У меня такое же поведение с вашим кодом, но, как ни странно, нет предупреждения / ошибки, заменяющего

return dynamic_cast<const Base*>(&object) != nullptr;

от

const T * pobject = &object;

return std::is_same<Base, T>::value || dynamic_cast<const Base*>(pobject) != nullptr;

#include <memory>

class A {
public:
    virtual ~A() {}
};
class B : public A {
public:
    virtual ~B() {}
};

template <typename Base, typename T>
inline bool isInstanceOf(const T& object) {
  const T * pobject = &object;

  return std::is_same<Base, T>::value || dynamic_cast<const Base*>(pobject) != nullptr;
}

int main() {
    isInstanceOf<A>(B());
    isInstanceOf<A>(A());   // Compilation fails

    return 0;
}

Компиляция (gcc версия 6.3.0)

pi@raspberrypi:/tmp $ g++ -pedantic -Wextra -Wall -Werror b.cc
pi@raspberrypi:/tmp $ 
...