简体   繁体   中英

std::function, literal types and templates

I'm working on C++17 project which among others have these definitions (in my namespace, of course):

using CppFunction = std::function<int(StatePtr&)>;

template<typename T>
using CppMethod = std::function<int(T*, StatePtr&)>;

Now I declare some functions:

template<typename... Targs, typename F>
constexpr CppFunction CppFunctionNative(F func);

template<typename... Targs, typename F, typename T = (deducing class from F here) >
constexpr CppMethod<T> CppMethodNative(F func);

To my surprise, first declaration cause compiler error Constexpr function's return type is not a literal type , but second works just fine.

Why does this happen? Is it related to the fact that second function's return type is template?

UPD: simplified standalone example:

#include <functional>

using CppFunction = std::function<int(int&)>;

template<typename T>
using CppMethod = std::function<int(T*, int&)>;

// Clang will complain
template<typename F>
constexpr CppFunction CppFunctionNative(F func) {};

// Clang will not complain
template<typename F, typename T = /*(deducing class from F here)*/char >
constexpr CppMethod<T> CppMethodNative(F func) {};

// main() function doesn't matter
int main() {
    CppFunctionNative(0);
    CppMethodNative(0);
    return 0;
};

Interestingly enough, OnlineGDB and offline GCC 8.3.0 doesn't complain about this, but my Clang 8 does.

To my surprise, first declaration cause compiler error Constexpr function's return type is not a literal type, but second works just fine.

Why does this happen? Is it related to the fact that second function's return type is template?

Yes. In the first case, the compiler can prove that that specific specialization of std::function is not a literal type.

In the second case, it must know T before it knows if the specific specialization of std::function is a literal type.

template<class T>
struct example {
  virtual T get() const {return {};}
  ~example() {}
};
template<>
struct example<int> {};

#if 0
template<class F>
constexpr example<double> test( F ) { return {}; }
#endif

template<class T, class F>
constexpr example<T> test2( F ) { return {}; }

Live example

test is illegal; test2 is fine. In fact, test2<int>( 3.14 ) is a legal constexpr call.

Now, std::function has no such specialization , but that is not something the compiler is required to prove; doing so in general requires solving the halting problem, which is something C++ standard tries to avoid asking compilers to do.

As mentioned by @MaxLanghof, this kind of error results in an "ill-formed, no diagnostic required" program. The compiler is not forced to detect that your constexpr function cannot be constexpr , but if you do that the compiler is free to do anything, including generate an error message (or worse things).

So GCC is not wrong to omit the error, and clang is not wrong to emit an error. Clang's error emission is, in a sense, a higher QoI than gcc's omission.

Edit: Yakk's answer is more complete - one has to of course consider the theoretical possibility of std::function specializations!

constexpr means "there is at least one set of arguments for which this function produces a constant expression". If no such arguments exist, the program is ill-formed, no diagnostic required . This means it is up to the compiler whether it completely ignores this issue, whether it only complains at instantiation-time or whether it already barks at the declaration.

clang is evidently smart/eager enough to realize that a function template with a non-literal return type can never be constexpr . gcc does not seem to bother checking this - it's not required to. Your program is still wrong though - std::function is not a literal type and can thus never be returned from a constexpr function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM