【问题标题】:Compilation error with std::is_same and operator ||使用 std::is_same 和运算符 || 的编译错误
【发布时间】:2023-04-06 16:58:01
【问题描述】:

我不明白为什么下面的代码可以用 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;

【问题讨论】:

    标签: c++11 g++ clang++


    【解决方案1】:

    (这不是一个“真实”的答案,但使用答案比使用评论更实用)

    我的代码有相同的行为,但奇怪的是没有警告/错误替换

    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 $ 
    

    【讨论】:

    • 可能编译器只是通过它的 AST 看不够远,无法破译 pobject 来自同一类型的 &amp; 的事实。引用不假定为空,而指针当然可能...
    【解决方案2】:

    这段代码不是给你一个错误,而是一个警告。在我看来,它在这里很有帮助。警告是您可以选择忽略的东西,但在这种情况下,它提出了一个有用的观点: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 有缺陷的情况,告诉你在某些模板实例化下无法执行的代码。

    【讨论】:

      【解决方案3】:

      以下代码也可以在没有警告的情况下编译:

      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)

      目前我选择了“三元运算符”变体,但我更喜欢“两个函数”的变体。

      【讨论】:

        【解决方案4】:

        我认为 c++17 解决方案也可以

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

        【讨论】:

          猜你喜欢
          • 2016-07-05
          • 1970-01-01
          • 2021-10-15
          • 2012-08-13
          • 1970-01-01
          • 2012-05-03
          • 2017-05-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多