简体   繁体   中英

Unable to get type of function returning constexpr auto

I'm trying to use a property system in a class.

A property has the corresponding member pointer, a name and an int (from an enum) to uniquely identify it.

Here is the code that defines a property:

template<typename Class, typename T>
struct MemberProperty
{
    constexpr MemberProperty(T Class::*aMember, const char* aName, int aId)
    : member(aMember), name(aName), id(aId)
    {}

    T Class::*member;
    const char* name;
    int id;
};

I create properties by calling this function:

template <typename Class, typename T>
constexpr auto makeProperty(T Class::*member, const char* name, int id) {
    return MemberProperty<Class, T>{member, name, id};
}

My goal is to define properties for a class like this:

class User
{
public:
    enum PropertiesEnum
    {
        Property_Name
    };

    string m_name;

    static constexpr auto Properties() {
        return std::make_tuple(
            makeProperty(&User::m_name, "name", User::Property_Name)
        );
    }

    using PropertiesType = decltype(Properties());

    //PropertyManager<PropertiesType> m_propertyManager;
};

I would like to be able to uncomment the line that declares m_propertyManager . The problem is that this doesn't compile. In g++, I get:

error: use of 'static constexpr auto User::Properties()' before deduction of 'auto'

In Visual Studio 2015, I get:

error C3779: 'User::UserProperties': a function that returns 'auto' cannot be used before it is defined

How can I make this work? It looks like a circulary dependency but I can't find how to make it work. Here is the example:

http://coliru.stacked-crooked.com/a/24e7f5ea7f83da6f

I guess it is due to [class.mem]/6 .
It states that:

A class is considered a completely-defined object type ([basic.types]) (or complete type) at the closing } of the class-specifier. [...]

Note the last statement for your specific case:

[...] Otherwise it is regarded as incomplete within its own class member-specification.

Alias declarations are considered part of the member-specification and are not mentioned in the (let me say) exceptions to the rule:

[...] Within the class member-specification, the class is regarded as complete within function bodies, default arguments, exception-specifications, and default member initializers (including such things in nested classes). [...]

By reducing a bit further your example, we have this:

struct S {
    auto f() {}
    using T = decltype(f());
};

int main() {}

The error is more or less the same.

At the using declaration, as mentioned above, the class is not considered a completely-defined type, thus are not its member functions.
Because of that, deduction for the return type of the member function can't take place and the using declaration cannot be satisfied.
Note that to deduce the return type the compiler must look at the definition of the function, that is at its body.

In other terms, it's not (conceptually) far from doing this:

auto f();
using T = decltype(f());
int main() {}

How can you evaluate the return type of a function that has not been defined yet?
You cannot and the code above doesn't work indeed.

The fact that the member function is a constexpr one doesn't change anything in this case.


As mentioned in the comments to the question, you can explicitly specify the return type by means of a trailing return type to work around the issue.
In that case, a definition is no longer required and you can get the return type out of the declaration. Actually, there will be no deduction at all for the return type.

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