Почему нельзя использовать auto в качестве параметра типа шаблона?

Я играл с ключевым словом С++ 0x auto и пробовал следующее.

std::unique_ptr<auto> ptr(new int(0));

Я попытался скомпилировать его с g++ 4.4.5 и получил

error: invalid use of auto

Судя по глазу, auto можно легко вывести на int.

Мое предположение - это вывод типа, и механизм шаблонов не разговаривает друг с другом. В противном случае механизм шаблона знал бы, чтобы создать экземпляр класса шаблона с int в качестве параметра типа.

Еще одно предположение из стандарта, я вижу это.

A member shall not be declared with auto, extern or register storage class.

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

И последнее мое предположение заключается в том, что компилятор считает, что это класс хранения auto, а не auto для вывода типа.

Есть ли причина, по которой это указано в стандарте?

Ответ 1

@dascandy правильно определил, что не так с вашим кодом. Я попытаюсь объяснить некоторые причины:

Вы ожидаете, что компилятор выведет unique_ptr<int>, потому что аргумент int*, а unique_ptr<int> имеет конструктор, который принимает int*. На мгновение пусть игнорирует тот факт, что мы используем std::unique_ptr, и просто говорим о классе шаблона, который мы написали (и можем специализироваться).

Почему компилятор должен вывести unique_ptr<int>? Аргумент не int, it int*. Почему бы не угадать unique_ptr<int*>? Конечно, это приведет к ошибке компилятора, так как конструктор unique_ptr<int*> не примет int*. Если я не добавлю специализацию:

template<>
class unique_ptr<int*>
{
public:
    unique_ptr(int*) {}
};

Теперь unique_ptr<int*> будет компилироваться. Как компилятор должен знать, что выбрать, unique_ptr<int> или unique_ptr<int*>? Что делать, если я добавлю другую специализацию?

template<>
class unique_ptr<double>
{
public:
    unique_ptr(int*) {}
};

Теперь у компилятора есть три варианта выбора, и он должен создать экземпляр шаблона с каждым возможным аргументом, чтобы найти их. Очевидно, что это невозможно, особенно с несколькими аргументами шаблона и рекурсией шаблонов.

Что вы можете сделать, это сделать функцию factory, которая соединяет выведенный тип с одним экземпляром шаблона:

template<typename T>
std::unique_ptr<T> make_unique(T* arg) { return arg; }

(конечно, это не сработает, потому что unique_ptr не может быть скопировано, но идея действительна и используется, например, make_shared и make_pair.)


Некоторые примеры крайнего уродства:

Можно утверждать, что unique_ptr<shared_ptr<int>> является допустимым соответствием для этого кода.

Или как насчет:

template<typename T>
class unique_ptr
{
public:
    explicit unique_ptr(T* arg);
    unique_ptr(int*, enable_if<(sizeof(T) > 16)>::type* = 0);
};

Ответ 2

Это потому, что он должен определить класс, с которым нужно вызвать конструктор, прежде чем определять, что делать с его аргументами. Если вы сделаете конструктор шаблоном, он будет работать как любая другая функция шаблона - автоматически выводить аргументы.

Ответ 3

Просто хочу добавить, что решение уже существует для большинства случаев:

template <typename T>
std::unique_ptr<T> unique_ptr_auto(T* ptr)
{
    // fails to handle std::unique_ptr<T[]>, not deducible from pointer
    return std::unique_ptr<T>(ptr);
}

auto ptr = unique_ptr_auto(new int(0));

Немного более многословный, очевидно, но вы получите эту идею. Эти "функции генератора" довольно распространены.

Ответ 4

Это (или подобное) было предложено для Стандарта. Предложенная функциональность выглядела примерно так:

std::vector<int> GetMahVector();
std::vector<auto> var = GetMahVector();

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