2016-11-01 2 views
3

Я пытаюсь использовать систему свойств в классе.Невозможно получить тип возвращаемой функции constexpr auto

Свойство имеет соответствующий член-указатель, имя и int (из перечисления), чтобы однозначно идентифицировать его.

Вот код, который определяет свойство:

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; 
}; 

я создаю свойства, вызвав эту функцию:

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

Моя цель заключается в определении свойств класса, как это:

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; 
}; 

Я хотел был бы иметь возможность раскомментировать линию, которая объявляет m_propertyManager , Проблема в том, что это не скомпилировано. В г ++, я получаю:

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

В Visual Studio 2015, я получаю:

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

Как я могу сделать эту работу? Это похоже на циркулирующую зависимость, но я не могу найти, как заставить ее работать. Вот пример:

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

+2

Я думаю '-> decltype (std :: make_tuple (makeProperty (& User :: m_name," name ", User :: Property_Name)))' в декларации помог ... [live demo] (http: // coliru.stacked-crooked.com/a/a7f291a384992464) –

+1

Спасибо, что все работает. Но есть ли другой путь? Параметры make_tuple потенциально очень большие (я добавлю много свойств) – Michael

+1

Обычно я использую MACRO, чтобы избежать повторения: '#define RETURN (ret) -> decltype (ret) {return ret; } ' – Jarod42

ответ

4

Я предполагаю, что это связано с [class.mem]/6.
Он утверждает, что:

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

Обратите внимание на последнее заявление для вашего конкретного случая:

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

декларации алиасов считаются частью члена-спецификации и не упоминаются в (позвольте мне сказать) исключения из этого правила:

[...] 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). [...]

за счет сокращения немного дальше ваш пример, мы имеем следующее:

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

int main() {} 

Ошибка будет примерно одинаковой.

В объявлении использования, как упоминалось выше, класс не считается полностью определенным типом, поэтому не являются его функциями-членами.
Из-за этого вычет для возвращаемого типа функции-члена не может быть выполнен, и использование объявления не может быть выполнено.
Обратите внимание, что для вывода возвращаемого типа компилятор должен посмотреть при определении функции, то есть в ее теле.

Другими словами, это не (концептуально) далеко от выполнения этого:

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

Как вы можете оценить тип возвращаемого функцией, которая не была определена еще?
Вы не можете и код выше не работает.

Тот факт, что функция-член является constexpr, ничего не меняет в этом случае.


Как уже упоминалось в комментариях к этому вопросу, вы можете явно указать тип возвращаемого значения с помощью возвращаемого типа трейлинг, чтобы обойти эту проблему.
В этом случае определение больше не требуется, и вы можете получить тип возврата из декларации. Фактически, для типа возврата не будет никакого вычета.