Возможно ли иметь переменную-член "auto"?

Например, я хотел иметь переменную типа auto, потому что я не уверен, какой тип она будет.

Когда я пытаюсь объявить его в объявлении class/struct, он дает мне эту ошибку:

Невозможно вывести автоматический тип. Требуется инициализатор

Есть ли способ?

struct Timer {

    auto start;

};

Ответ 1

Вы можете, но вы должны объявить его static и const:

struct Timer {
    static const auto start = 0;
};

Рабочий пример в Coliru.

При этом ограничении вы не можете иметь start как нестатический член и не можете иметь разные значения в разных объектах.

Если вам нужны разные типы start для разных объектов, лучше иметь свой класс в качестве шаблона

template<typename T>
struct Timer {
    T start;
};

Если вы хотите вывести тип T, вы можете сделать factory -подобную функцию, которая делает вывод типа.

template<typename T>
Timer<typename std::decay<T>::type> MakeTimer(T&& startVal) {   // Forwards the parameter
   return Timer<typename std::decay<T>::type>{std::forward<T>(startVal)};
}

Живой пример.

Ответ 2

Вот что говорит черновик стандарта C++ об использовании auto для переменных-членов, в разделе 7.1.6.4 auto specifier 4 параграф 4:

Автоматический спецификатор типа также можно использовать при объявлении переменной в условии оператора выбора (6.4) или оператора итерации (6.5), в описании типа-seq в идентификаторе нового типа или идентификаторе типа new-выражение (5.3.4) в объявлении for-range и в объявлении статического члена данных с инициализатором фигурной скобки или равным скобкам, который появляется в спецификации члена определения класса (9.4.2),

Поскольку он должен быть инициализирован, это также означает, что он должен быть const. Так что что-то вроде следующего будет работать:

struct Timer
{
  const static int start = 1;
}; 

Я не думаю, что это слишком много тебя заводит. Использование шаблона, как предлагает Марк, или теперь, когда я подумаю об этом, может быть, вам просто нужен вариантный тип. В этом случае вы должны проверить Boost.Variant или Boost.Any.

Ответ 3

Нет. Каждый конструктор может иметь свой собственный инициализатор для start, поэтому не может быть согласованного типа.

Если у вас есть полезное выражение, вы можете использовать его:

struct Timer {

   Foo getAFoo();

   delctype(Timer().getAFoo().Bar()) start;

   Timer() : start(getAFoo().Bar()) { /***/ }
};

Ответ 4

Косвенно, при условии, что вы не ссылаетесь на члена класса.

Теперь это также может быть достигнуто с помощью руководств по выводам, они были введены в С++ 17 и недавно (наконец) была добавлена поддержка в VC++ (у clang и GCC она уже была).

https://en.cppreference.com/w/cpp/language/class_template_argument_deduction

Например:

template <typename>
struct CString;

template <typename T, unsigned N>
struct CString<std::array<T, N>>
{
    std::array<T, N> const Label;

    CString(std::array<T, N> const & pInput) : Label(pInput) {}
};

template <typename T, std::size_t N>
CString(std::array<T, N> const & pInput) -> CString<std::array<T, N>>;

https://godbolt.org/z/LyL7UW

Это может быть использовано для вывода типов членов класса аналогично auto. Хотя переменные-члены должны как-то зависеть от аргументов конструктора.