Why can't auto be used as a template type parameter?
I've been playing around with C++0x's auto
keyword and tried the following.
std::unique_ptr<auto> ptr(new int(0));
I tried compiling it with g++ 4.4.5 and got
error: invalid use of auto
Judging by eye, auto
can easily be inferred to int
.
My gues开发者_JS百科s is the type inference and the template engine don't talk to each other. Otherwise, the template engine would know to instantiate the template class with int
as the type parameter.
Another guess is from the standard, I see this.
A member shall not be declared with auto, extern or register storage class.
But I thought that was the auto
as in local variables, not as in auto
used to deduce types.
And my last guess is that the compiler thinks this is an auto
storage class, not auto
for type deduction.
Is there a reason behind this stated in the standard?
That's because it has to determine the class on which to call a constructor before determining what to do with its arguments. If you make the constructor a template, it'll just work like any other template function - auto-deducing arguments.
@dascandy has correctly identified what's wrong with your code. I'll try to provide some rationale:
You're expecting the compiler to infer unique_ptr<int>
because the argument is an int*
, and unique_ptr<int>
has a constructor which accepts int*
. For a moment let's ignore the fact that we're using std::unique_ptr
, and just talk about a template class we wrote (and can specialize).
Why should the compiler infer unique_ptr<int>
? The argument isn't int
, it's int*
. Why shouldn't it guess unique_ptr<int*>
? Of course that would result in a compiler error, since unique_ptr<int*>
's constructor won't accept an int*
. Unless I add a specialization:
template<>
class unique_ptr<int*>
{
public:
unique_ptr(int*) {}
};
Now unique_ptr<int*>
would compile. How should the compiler know which to choose, unique_ptr<int>
or unique_ptr<int*>
? What if I add another specialization?
template<>
class unique_ptr<double>
{
public:
unique_ptr(int*) {}
};
The compiler now has three options to choose from, and it has to instantiate the template with every possible argument in order to find them. Clearly this is not feasible, especially with multiple template arguments and template recursion.
What you can do, is make a factory function which connects the inferred type to exactly one template instance:
template<typename T>
std::unique_ptr<T> make_unique(T* arg) { return arg; }
(of course, this won't work because unique_ptr
cannot be copied. But the idea is valid, and used in e.g.make_shared
and make_pair
.)
Some examples of extreme ugliness:
One could argue that unique_ptr<shared_ptr<int>>
is a valid match for this code.
Or how about:
template<typename T>
class unique_ptr
{
public:
explicit unique_ptr(T* arg);
unique_ptr(int*, enable_if<(sizeof(T) > 16)>::type* = 0);
};
Just want to add that a solution already exists for most cases:
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));
A bit more verbose, obviously, but you get the idea. These "generator functions" are quite common.
This (or similar) was proposed for the Standard. The proposed functionality looked something like:
std::vector<int> GetMahVector();
std::vector<auto> var = GetMahVector();
However, it was rejected. Why it was rejected, well, you'd have to dig up the relevant Standard process documents, if possible.
精彩评论