【问题标题】:Missing return statement after if-elseif-else 后缺少 return 语句
【发布时间】:2021-10-01 14:36:18
【问题描述】:

一些编译器(Intel icc、pgi/nvc++)对如下函数发出“缺少返回语句”警告,而其他编译器(gcc、clang)即使使用-Wall -Wextra -pedantic也不会发出警告:

下面的代码按照标准合法吗?

这是给出警告的我的代码的最小可重现示例。将其简化为,例如,仅使用一个函数即可消除警告。

// test.cpp
#include <climits>
#include <cstddef>

template<class T, std::size_t N>
class Test
{
public:

    class Inner;

private:
    static constexpr std::size_t NB_ = sizeof(std::size_t) * CHAR_BIT;
    static constexpr std::size_t NI_ = (N + NB_ - 1) / NB_;
};

template<class T, std::size_t N>
class Test<T, N>::Inner
{
public:

    Inner() : b_{0}, j_{0} {}

    friend bool operator!= (Inner x, Inner y)
    {
        if constexpr(J_ > 0)
            return x.j_ != y.j_ || x.b_ != y.b_;
        else
            return x.b_ != y.b_;
    }

private:
    static constexpr std::size_t J_ = NI_ - 1;
    std::size_t b_;
    std::size_t j_;
};

int main()
{
    Test<int, 50>::Inner x, y;
    int a, b;
    x.b_ = a; y.b_ = b;
    x != y;
}

编译:

> nvc++ test.cpp -std=c++17
"test.cpp", line 30: warning: missing return statement at end of non-void function "operator!="
      }
      ^
          detected during instantiation of class "Test<T, N>::Inner [with T=int, N=50UL]" at line 41

【问题讨论】:

  • 来自 nvc++ 的错误警告,gcc/clang/msvc 不会发出警告。

标签: c++ c++17 language-lawyer


【解决方案1】:

C++ 标准是这么说的,见[stmt.return]/2:

从构造函数、析构函数或具有 cv 返回类型的函数的末尾流出 void 等效于没有操作数的 return。否则,超出main 以外的函数的末尾会导致未定义的行为

您的operator != 正是这样做的。它永远不会从函数的末尾流出,因为所有控制路径都以return 结尾。

因此代码是正确的,而编译器的诊断是错误的。

【讨论】:

    【解决方案2】:

    是否可以将这些项目设置为编译为早期的 C++ 标准?

    一些较旧的编译器不喜欢在代码块中间返回(也有一些编译器不喜欢在代码块开头以外的任何地方使用变量标识符)。

    对于那些抱怨的人,我只是编码为:

    ret_type f(...)
    {
        ret_type result = DEFAULT_VALUE; //default value defined elsewhere
        if (condition)
            result = value_1; //presumed valid ret_type value
        else
            result = value_2; //presumed valid ret_type value
        
        return result;
    }
    

    【讨论】:

    • 标准 C++ 的任何版本都不是这样,这样的编译器完全是垃圾。根据您的编码指南,您可能会从静态分析工具中收到有关多个返回点的警告......但即便如此,该工具也不应该生成问题中显示的警告。
    • 另外,constexpr if 在代码中的存在排除了任何古老的语言版本。
    【解决方案3】:

    为了return,我有一些函数只能接受特定结果,这正是您的设置。如果确实是一个小众案例,其中两个if 语句都是错误的(强调小众),那么就会出现问题并且可以抛出异常。

    就我而言,在最后一个 if 子句之后,我添加:

    throw std::invalid_argument("\nx  and y cannot be compared\n)");
    

    【讨论】:

      【解决方案4】:

      要让您的代码在没有警告的情况下编译(英特尔 ICC 2021.3.0),请使用 __builtin_unreachable() 告诉编译器控制流永远不会到达终点:

      friend bool operator!= (Inner x, Inner y)
      {
          if constexpr(J_ > 0)
              return x.j_ != y.j_ || x.b_ != y.b_;
          else
              return x.b_ != y.b_;
          __builtin_unreachable();
      }
      

      【讨论】: