Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clang complains about undefined constexpr function in unevaluated context

I’m using a simple SFINAE trick to check whether a member function exists, like so:

#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()");

(I’m aware that there’s a simpler method now, but wasn’t back when I wrote this piece of code.)

This code works on GCC. Clang however issues a warning1 (all versions up to Apple LLVM 7.3, whichever that is upstream):

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;

In other word, clang expects the functions to be defined, not just declared, even though they are never called (only in the unevaluated context of decltype).

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?


1 Which is a problem as I’m compiling with -Werror. There are some warnings that are based on heuristics and therefore have unavoidable false positives, but this isn’t the case here as far as I can see.

like image 624
Konrad Rudolph Avatar asked Aug 17 '16 13:08

Konrad Rudolph


2 Answers

If you aren't intending ever to call a function, there's no point marking it constexpr.

constexpr on a function isn't visible to the type system (well, except that pre-C++14 it had the side effect of making a non-static member function const); rather, it's a promise that for at least one combination of template type arguments and function arguments (and object state, for a non-static member function) the body of the function can be evaluated as a constant expression (or an algorithm equivalent to a constant expression). Conversely, the lack of constexpr on a function is an instruction to the compiler not to even attempt to evaluate the function body as a constant expression.

Clang isn't correct, exactly, but it isn't incorrect, either, as you've specifically asked it to reject valid programs (with -Werror). You should take the warning-error as a strong hint that constexpr functions without a definition are a bad idea.

like image 154
ecatmur Avatar answered Sep 28 '22 20:09

ecatmur


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?

Compiler warnings are outside the C++ standard. Warnings are issued for valid programs that contain indications of human error. In your particular case, you chose to qualify with a constexpr a function that you did not define. Programs using that class will be valid only if you never call that function. If that's really the case then constexpr is not needed. But what if you intended to call that function (though forgot to provide an implementation for it), but due to some mistake (complex overload resolution, or just a stupid typo) a different function was called?

Thus Clang has its point in issuing the warning. However, it is arguable whether that situation deserves a warning; personally I wouldn't file a bug against GCC.

like image 39
Leon Avatar answered Sep 28 '22 20:09

Leon