Clang 抱怨未评估的上下文中未定义的 constexpr 函数

Clang complains about undefined constexpr function in unevaluated context

我正在使用一个简单的 SFINAE 技巧来检查成员函数是否存在,如下所示:

#include <type_traits>

template <typename C>
struct has_size {
    template <typename T>
    static constexpr auto check(T*) ->
        decltype(std::declval<T const>().size(), std::true_type{});

    template <typename>
    static constexpr auto check(...) -> std::false_type;

    static constexpr bool value = decltype(check<C>(nullptr))::value;
};

// Usage:
static_assert(has_size<std::vector<int>>::value, "std::vector<int> has size()");

(我现在知道there’s a simpler method,但是写这段代码的时候还没回来。)

此代码适用于 GCC。然而,Clang 发出警告1(Apple LLVM 7.3 之前的所有版本,以上游版本为准):

decltype.cpp:15:27: error: inline function 'has_size<std::__1::vector<int, std::__1::allocator<int> > >::check<std::__1::vector<int, std::__1::allocator<int> > >' is not defined [-Werror,-Wundefined-inline]
    static constexpr auto check(T*) ->
                      ^
decltype.cpp:22:44: note: used here
    static constexpr bool value = decltype(check<C>(nullptr))::value;

换句话说,clang 期望函数被定义,而不仅仅是声明,即使它们从未被调用(仅在 decltype 的未计算上下文中)。

这是 clang 中的错误吗?还是抱怨是对的?如果是这样,GCC 是否也正确接受此代码?

此外,在写这个问题时,我意识到可以通过删除成员函数模板前面的 constexpr 限定符来完全避免 clang 编译错误。 constexpr 的存在在这里改变了什么?


1 这是一个问题,因为我正在使用 -Werror 进行编译。有一些警告是基于启发式的,因此存在不可避免的误报,但据我所知,这里的情况并非如此。

Is this a bug in clang? Or is it right to complain? If so, is GCC also correct in accepting this code?

Furthermore, while writing this question I realised that the clang compilation error can be avoided altogether by removing the constexpr qualifier in front of the member function template. What does the presence of constexpr change here?

编译器警告超出了 C++ 标准。对包含人为错误指示的有效程序发出警告。在您的特定情况下,您选择使用 constexpr 限定您未定义的函数。使用 class 的程序只有在您从不调用该函数时才有效。如果确实如此,则不需要 constexpr。但是,如果您打算调用该函数(尽管忘记为其提供实现),但由于某些错误(复杂的重载解析,或者只是一个愚蠢的拼写错误)调用了一个不同的函数怎么办?

因此,Clang 发出警告是有道理的。然而,这种情况是否值得警告是有争议的;就我个人而言,我不会针对 GCC 提交错误。

如果您不打算调用函数,则标记它没有意义 constexpr

函数上的

constexpr 对类型系统不可见(好吧,除了在 C++14 之前它有制作非静态成员函数的副作用 const );相反,这是一个承诺,对于模板类型参数和函数参数(以及对象状态,对于非静态成员函数)的至少一种组合,函数的主体可以 evaluated 作为常量表达式(或等同于常量表达式的算法)。相反,函数上缺少 constexpr 是对编译器的指示,甚至不要尝试将函数体作为常量表达式求值。

Clang 确实正确,但也不是不正确,正如您明确要求的那样拒绝有效程序(-Werror)。您应该将警告错误视为一个强烈提示,即 constexpr 没有定义的函数是个坏主意。