Search code examples
c++gccc++17sfinaecompiler-bug

Can calls to member functions of function parameters be used as template arguments?


#include <type_traits>

template<class T>
auto f(T x) -> std::enable_if_t<x.size() == 4>;

is valid for Clang and MSVC, but invalid according to GCC. Which is correct? https://godbolt.org/z/q8v4G5vn6


Solution

  • Yes, the above code is well-formed and this is an instance of the GCC bug 80242 you have found.

    x is type-dependent on T, and x.size() can be a constant expression for some x. A compiler should never dismiss this code as ill-formed before instantiating f, because x.size() is possibly a constant expression.

    Even if x is an object that doesn't exist at compile time, we can access its size:

    std::array<int, 10> x;
    constexpr std::size_t size = x.size();
    

    ... and all major compilers accept this, as they should. The reason why this is okay to write is that .size() for std::array never accesses the value or address of the object x, only the template arguments that std::array was given. This means that in your function, if T = std::array<...>, then x.size() is a constant expression and should be usable as a template argument for std::enable_if_t.

    The error output for GCC is also totally nonsensical, which further reinforces the idea that this is a compiler bug:

    <source>:5:46: error: template argument 1 is invalid
        5 | auto f(T x) -> std::enable_if_t<x.size() == 4>;
          |                                              ^
    <source>:5:46: error: template argument 1 is invalid
    <source>:5:46: error: template argument 1 is invalid
    <source>:5:46: error: template argument 1 is invalid
    <source>:5:46: error: template argument 1 is invalid
    <source>:5:16: error: invalid use of template-name 'std::enable_if_t' without an argument list
        5 | auto f(T x) -> std::enable_if_t<x.size() == 4>;
          |                ^~~