C++0x error with constexpr and returning template

2019-02-16 14:35发布

问题:

I tried to find a solution for the problem of the question C++ template non-type parameter type deduction, which does not involve a template parameter to call f, but implicitly chooses the correct type for the template parameter.

Since constexpr should guarantee that a function only contains compile time constants, and is evaluated at compile time (at least thats what i think it does), i thought it might be the solution for this issue. So i came up with this:

template <class T, T VALUE> void f() {}

//first i tried this:
template <class T> auto get_f(T t) -> decltype( &f<T,t> ) { return f<T,t>; }

//second try:
template <class T> constexpr void (&get_f( T t ))()  { return f<T,t>; }

int main()
{
    get_f(10)(); //gets correct f and calls it
}

first version generates following error:

error: use of parameter 't' outside function body

which is really confusing, since the usage of parameters in the decltype statement of a trailing return type should be ok?

second version generates following error:

error: invalid initialization of non-const reference of type 'void (&)()' 
       from an rvalue of type '<unresolved overloaded function type>'

which is kinda confusing, since i fully qualified f in get_f. I would expect this kind of error messages if i did not have the constexpr. So do i have a false understanding of what constexpr does, or is the C++0x implementation of GCC flawed for this case ?

I am using GCC 4.6.2

回答1:

Since constexpr should guarantee that a function only contains compile time constants, and is evaluated at compile time (at least thats what i think it does), i thought it might be the solution for this issue.

A constexpr function can be used in a constant expression context, but is not restricted to one. In this respect they are different from a metafunction and a regular function. Consider the problem of returning the successor of an integer:

// Regular function
int f(int i)
{ return i + 1; }

// Regular metafunction
template<int I>
struct g {
    static constexpr auto value = I + 1;
};

// constexpr function
constexpr int h(int i)
{ return i + 1; }

// Then...
{
    // runtime context: the metafunction can't be used
    int i;
    std::cin >> i;

    f(i); // Okay
    g<i>::value; // Invalid
    h(i); // Okay

    // compile time context: the regular function can't be used
    char a[f(42)]; // Invalid
    char b[g<42>::value]; // Okay
    char c[h(42)]; // Okay
}

constexpr has other usages (e.g. constructors) but when it comes to constexpr functions this is the gist of it: some functions should be available in both runtime and constant contexts because some computations are available in both. It's possible to compute i + 1 whether i is a compile-time constant or is extracted from std::cin.

This means that inside the body of a constexpr function the parameters are not themselves constant expressions. So what you are attempting is not possible. Your function can't deal with

int i;
std::cin >> i;
get_f(i); // what's the return type?

and the violation happens here:

constexpr auto get_f(T t)
-> decltype( &f<T,t> ) // <-

Since t is not a constant expression according to the rules of the language (no matter what, even if you actually only pass constant expressions in), it can't appear as the second template parameter of f.

(And in the larger picture it means that no, you can't use argument deduction from function templates to conveniently pass a non-type parameter to a class template.)