c++templatesc++-conceptsdeclval

Why doesn't this requires clause return true when using declval?


I'm trying to use a requires clause, and it only works if used on an actual concrete object passed as an argument to a function, but not one given by declval:

struct ImageMipInfo
{
    ImageMipInfo() {}
    ImageMipInfo(int a) 
    {}
    static inline constexpr int JUST_SERIALISE_ME_BY_MEMCPYING{};
};

template <typename T>
static constexpr bool isTypeMemcopyableUsingArgumentObject(T& arg)
{
    constexpr bool bHasMemcpyOverride = requires { arg.JUST_SERIALISE_ME_BY_MEMCPYING; };

    return bHasMemcpyOverride;
}

template <typename T>
static constexpr bool isTypeMemcopyableUsingDeclVal()
{
    constexpr bool bHasMemcpyOverride = requires { declval<T>().JUST_SERIALISE_ME_BY_MEMCPYING; };
    return bHasMemcpyOverride;
}
  
int main() {

    ImageMipInfo mip_info;
    static_assert(isTypeMemcopyableUsingArgumentObject(mip_info)); // This passes
    static_assert(isTypeMemcopyableUsingDeclVal<ImageMipInfo>()); // This doesn't pass

}

The error I get on Clang is:

error: static assertion failed due to requirement

And in GCC:

error: there are no arguments to 'declval' that depend on a template parameter, so a declaration of 'declval' must be available

Link on Godbolt


Solution

  • You believe the expression declval<ImageMipInfo>().JUST_SERIALISE_ME_BY_MEMCPYING; is well-formed, and therefore the requires clause should be true.

    Let's put that expression directly in main(), to force the compiler to describe what might be wrong!

    int main() 
    {
        declval<ImageMipInfo>().JUST_SERIALISE_ME_BY_MEMCPYING;
    }
    
     In function 'int main()':
     error: 'declval' was not declared in this scope; did you mean 'std::declval'?
          |     declval<ImageMipInfo>().JUST_SERIALISE_ME_BY_MEMCPYING;
          |     ^~~~~~~~~~~~~~~~~~~~~
          |     std::declval
    

    The correct spelling is std::declval, which is defined in header <utility>.

    Your requires clause fails because declval is not defined in your code, which makes declval<T>().JUST_SERIALISE_ME_BY_MEMCPYING; ill-formed.

    #include <utility>
    
    struct ImageMipInfo
    {
        ImageMipInfo() {}
        ImageMipInfo(int a) 
        {}
        static inline constexpr int JUST_SERIALISE_ME_BY_MEMCPYING{};
    };
    
    template <typename T>
    static constexpr bool isTypeMemcopyableUsingArgumentObject(T& arg)
    {
        constexpr bool bHasMemcpyOverride = requires { arg.JUST_SERIALISE_ME_BY_MEMCPYING; };
    
        return bHasMemcpyOverride;
    }
    
    template <typename T>
    static constexpr bool isTypeMemcopyableUsingDeclVal()
    {
        constexpr bool bHasMemcpyOverride = requires { std::declval<T>().JUST_SERIALISE_ME_BY_MEMCPYING; };
        return bHasMemcpyOverride;
    }
    
    int main() {
    
        ImageMipInfo mip_info;
        static_assert(isTypeMemcopyableUsingArgumentObject(mip_info)); // This passes
        static_assert(isTypeMemcopyableUsingDeclVal<ImageMipInfo>()); // This DOES PASS NOW
    }
    

    https://godbolt.org/z/r7EcbG7n8