I am practicing C++ by building my own version of vector, named "Vector". I have two constructors among others, fill constructor and range constructor. Their declarations look like following:
template <typename Type>
class Vector {
public:
// fill constructor
Vector(size_t num, const Type& cont);
// range constructor
template<typename InputIterator> Vector(InputIterator first, InputIterator last);
/*
other members
......
*/
}
The fill constructor fills the container with num of val; and the range constructor copies every value in the range [first, last) into the container. They are supposed to be the same with the two constructors of the STL vector.
Their definitions goes following:
//fill constructor
template <typename Type>
Vector<Type>::Vector(size_t num, const Type& cont){
content = new Type[num];
for (int i = 0; i < num; i ++)
content[i] = cont;
contentSize = contentCapacity = num;
}
// range constructor
template <typename Type>
template<typename InputIterator>
Vector<Type>::Vector(InputIterator first, InputIterator last){
this->content = new Type[last - first];
int i = 0;
for (InputIterator iitr = first; iitr != last; iitr ++, i ++)
*(content + i) = *iitr;
this->contentSize = this->contentCapacity = i;
}
However, when I try to use them, I have problem distinguishing them.
For example:
Vector<int> v1(3, 5);
With the this line of code, I intended to create a Vector that contains three elements, each of which is 5. But the compiler goes for the range constructor, treating both "3" and "5" as instances of the "InputIterator", which, with no surprises, causes error.
Of course, if I change the code to:
Vector<int> v1(size_t(3), 5);
Everything is fine, the fill constructor is called. But that is obviously not intuitive and user friendly.
So, is there a way that I can use the fill constructor intuitively?
You can use std::enable_if (or boost::enable_if if you don't use C++11) to disambiguate the constructors.
#include <iostream>
#include <type_traits>
#include <vector>
using namespace std;
template <typename Type>
class Vector {
public:
// fill constructor
Vector(size_t num, const Type& cont)
{
cout << "Fill constructor" << endl;
}
// range constructor
template<typename InputIterator> Vector(InputIterator first, InputIterator last,
typename std::enable_if<!std::is_integral<InputIterator>::value>::type* = 0)
{
cout << "Range constructor" << endl;
}
};
int main()
{
Vector<int> v1(3, 5);
std::vector<int> v2(3, 5);
Vector<int> v3(v2.begin(), v2.end());
}
The above program should first call the fill constructor by checking if the type is an integral type (and thus not an iterator.)
By the way, in your implementation of the range constructor, you should use std::distance(first, last) rather than last - first. Explicitly using the - operator on iterators limits you to RandomAccessIterator types, but you want to support InputIterator which is the most generic type of Iterator.
Even std::vector seems to have this issue.
std::vector<int> v2(2,3);
chooses
template<class _Iter>
vector(_Iter _First, _Iter _Last)
In Visual C++, even though it should match closer to the non templated case..
Edit: That above function (correctly) delegates the construction to the below one. I am totally lost..
template<class _Iter>
void _Construct(_Iter _Count, _Iter _Val, _Int_iterator_tag)
Edit #2 AH!:
Somehow this below function identifies which version of the constructor is meant to be called.
template<class _Iter> inline
typename iterator_traits<_Iter>::iterator_category
_Iter_cat(const _Iter&)
{ // return category from iterator argument
typename iterator_traits<_Iter>::iterator_category _Cat;
return (_Cat);
}
The above shown _Construct function has (atleast) 2 versions overloading on the third variable which is a tag to returned by the above _Iter_cat function. Based on the type of this category the correct overload of the _Construct is picked.
Final edit:
iterator_traits<_Iter> is a class that seems to be templated for many different common varieties, each returning the appropriate "Category" type
Solution: It appears template specialization of the first arguement's type is how the std library handles this messy situation (primitive value type) in the case of MS VC++. Perhaps you could look into it and follow suit?
The problem arises (I think) because with primitive value types, the Type and size_t variables are similar, and so the template version with two identical types gets picked.
The problem is the same at the one faced by the standard library implementation. There are several ways to solve it.
You can meticulously provide non-template overloaded constructors for all integral types (in place of the first parameter).
You can use SFINAE-based technique (like enable_if) to make sure the range constructor is not selected for integer argument.
You can branch the range constructor at run-time (by using if) after detecting integral argument (by using is_integral) and redirect control to the proper constructing code. The branching condition will be a compile-time value, meaning that the code will probably be reduced at compile-time by the compiler.
You can simply peek into your version of standard library implementation and see how they do it (although their approach is not required to be portable and/or valid from the point of view of abstract C++ language).
This ambiguity caused problems for early library implementers. It's called the "Do The Right Thing" effect. As far as I know, you need SFINAE to solve it… it might have been one of the first applications of that technique. (Some compilers cheated and hacked their overload resolution internals, until the solution was found within the core language.)
The standard specification of this issue is one of the key differences between C++98 and C++03. From C++11, §23.2.3:
14 For every sequence container defined in this Clause and in Clause 21:
— If the constructor
template <class InputIterator>
X(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type())
is called with a type InputIterator that does not qualify as an input iterator, then the constructor shall not participate in overload resolution.
15 The extent to which an implementation determines that a type cannot be an input iterator is unspecified, except that as a minimum integral types shall not qualify as input iterators.
Related
I suspect the prototypes of fill constructor and range constructor of std::vector (and many other STL types) given in this webpage are not right, so I implement a NaiveVector to mimic these two prototypes.
My code is:
#include <iostream>
#include <vector>
using namespace std;
template <typename T>
struct NaiveVector {
vector<T> v;
NaiveVector(size_t num, const T &val) : v(num, val) { // fill
cout << "(int num, const T &val)" << endl;
}
template <typename InputIterator>
NaiveVector(InputIterator first, InputIterator last) : v(first, last) { // range
cout << "(InputIterator first, InputIterator last)" << endl;
}
size_t size() const { return v.size(); }
};
int main() {
NaiveVector<int> myVec1(5,1); // A
cout << "size = " << myVec1.size() << endl;
for (auto n : myVec1.v) { cout << n << " "; }
cout << endl;
cout << "-----" << endl;
vector<int> vec({1,2,3,4,5});
NaiveVector<int> myVec2(vec.begin(), vec.end());// B
cout << "size = " << myVec2.size() << endl;
for (auto n : myVec2.v) { cout << n << " "; }
cout << endl;
}
And the output is:
$ g++ overload.cc -o overload -std=c++11
$ ./overload
(InputIterator first, InputIterator last) // should be: (int num, const T &val)
size = 5
1 1 1 1 1
-----
(InputIterator first, InputIterator last)
size = 5
1 2 3 4 5
As I suspected from the beginning, the compiler cannot differentiate the two constructors properly. Then my question is: how does std::vector's fill constructor and range constructor differentiate from each other?
Rephrase: how to implement the two constructors of this NaiveVector?
This question seems to be a duplicate of this question but the answer is not satisfying. Additionally, C++11 itself doesn't provide a is_iterator<>.. (MSVC has lots of hacks).
Edit: it is allowed to bind an rvalue to a constant lvalue reference, so the first constructor of NaiveVector is valid for A.
C++03
[lib.sequence.reqmts]/9
For every sequence defined in this clause and in clause 21:
the constructor
template <class InputIterator>
X(InputIterator f, InputIterator l, const Allocator& a = Allocator())
shall have the same effect as:
X(static_cast<typename X::size_type>(f),
static_cast<typename X::value_type>(l),
a)
if InputIterator is an integral type.
...
[lib.sequence.reqmts]/11
One way that sequence implementors can satisfy this requirement is to specialize the member template for
every integral type. Less cumbersome implementation techniques also exist.
In other words, the standard says that if the range constructor gets selected by overload resolution but the "iterator" type is actually an integral type, it has to delegate to the fill constructor by casting its argument types to force the latter to be an exact match.
C++11/C++14
[sequence.reqmts]/14
For every sequence container defined in this clause and in clause 21:
If the constructor
template <class InputIterator>
X(InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type())
is called with a type InputIterator that does not qualify as an input iterator, then the constructor shall not participate in overload resolution.
...
[sequence.reqmts]/15
The extent to which an implementation determines that a type cannot be an input iterator is unspecified,
except that as a minimum integral types shall not qualify as input iterators.
This is more or less the way the standard hints to you to use SFINAE (which works reliably in C++11 as opposed to C++03).
C++17
The wording is similar, except that the paragraph about integral types not being iterators has been moved to [container.requirements.general]/17.
Conclusion
You can write your range constructor to look something like this:
template <typename InputIterator,
typename = std::enable_if<is_likely_iterator<InputIterator>>::type>
NaiveVector(InputIterator first, InputIterator last)
The is_iterator helper template might simply disqualify integral types and accept all other types, or it might do something more sophisticated such as checking whether there is an std::iterator_traits specialization that indicates that the type is an input iterator (or stricter). See libstdc++ source, libc++ source
Both approaches are consistent with the standard's mandated behaviour of std::vector in C++11 and later. The latter approach is recommended (and will be consistent with what the real std::vector is likely to do on typical implementations), because if you pass arguments that are implicitly convertible to the types expected by the fill constructor, you would hope that the class would "do the right thing" and not select the range constructor only to have it fail to compile. (Although I suspect this is fairly uncommon.) Relative to the C++03 std::vector, it is a "conforming extension" since in that case the constructor call would not compile.
If you read the documentation at http://en.cppreference.com/w/cpp/container/vector/vector carefully, you will notice the following (emphasis mine):
4) Constructs the container with the contents of the range [first, last).
This constructor has the same effect as vector(static_cast<size_type>(first), static_cast<value_type>(last), a) if InputIt is an integral type. (until C++11)
This overload only participates in overload resolution if InputIt satisfies InputIterator, to avoid ambiguity with the overload (2).
The confusion is not with the std::vector but your expectation of which of your constructors gets called. Your code doesn't have the checks to make sure that the second constructor gets called only if the above condition of std::vector is met.
In your case, when you use
NaiveVector<int> myVec1(5,1);
the second constructor can be called by using int as the template parameter without requiring any conversion. The compiler needs to convert an int to a size_t in order to be able to call the first constructor. Hence, the second constructor is a better fit.
You can try adding some type trait check to verify that arguments of second constructor are iterators over T elements:
template
<
typename InputIterator
, typename = typename ::std::enable_if
<
::std::is_same
<
T &
, typename ::std::remove_const
<
decltype(*(::std::declval< InputIterator >()))
>::type
>::value
, void
>::type
>
NaiveVector(InputIterator first, InputIterator last) : v(first, last)
{
cout << "(InputIterator first, InputIterator last)" << endl;
}
Run this code online
Consider the following:
using vector_type = std::vector<int>;
using const_iterator = typename vector_type::const_iterator;
using const_reverse_iterator = typename vector_type::const_reverse_iterator;
using iterator = typename vector_type::iterator;
using reverse_iterator = typename vector_type::reverse_iterator;
int main()
{
static_assert(!std::is_assignable_v<iterator, const_iterator>); // passes
static_assert(!std::is_assignable_v<reverse_iterator, const_reverse_iterator>); // fails
static_assert(std::is_assignable_v<reverse_iterator, const_reverse_iterator>); // passes
}
I can check that assignment of iterator{} = const_iterator{} is not valid, but not an assignment of reverse_iterator{} = const_reverse_iterator{} with this type trait.
This behavior is consistent across gcc 9.0.0, clang 8.0.0, and MSVC 19.00.23506
This is unfortunate, because the reality is that reverse_iterator{} = const_reverse_iterator{} doesn't actually compile with any of the above-mentioned compilers.
How can I reliably check such an assignment is invalid?
This behavior of the type trait implies that the expression
std::declval<reverse_iterator>() = std::declval<const_reverse_iterator>()
is well formed according to [meta.unary.prop], and this appears consistent with my own attempts at an is_assignable type trait.
This trait passes because the method exists that can be found via overload resolution and it is not deleted.
Actually calling it fails, because the implementation of the method contains code that isn't legal with those two types.
In C++, you cannot test if instantiating a method will result in a compile error, you can only test for the equivalent of overload resolution finding a solution.
The C++ language and standard library originally relied heavily on "well, the method body is only compiled in a template if called, so if the body is invalid the programmer will be told". More modern C++ (both inside and outside the standard library) uses SFINAE and other techniques to make a method "not participate in overload resolution" when its body would not compile.
The constructor of reverse iterator from other reverse iterators is the old style, and hasn't been updated to "not participate in overload resolution" quality.
From n4713, 27.5.1.3.1 [reverse.iter.cons]/3:
template<class U> constexpr reverse_iterator(const reverse_iterator<U>& u);
Effects: Initializes current with u.current.
Notice no mention of "does not participate in overload resolution" or similar words.
The only way to get a trait you want like this would be to
Change (well, fix) the C++ standard
Special case it
I'll leave 1. as an exercise. For 2., you know that reverse iterators are templates over forward iterators.
template<class...Ts>
struct my_trait:std::is_assignable<Ts...> {};
template<class T0, class T1>
struct my_trait<std::reverse_iterator<T0>, std::reverse_iterator<T1>>:
my_trait<T0, T1>
{};
and now my_trait is is_assignable except on reverse iterators, where it instead tests assignability of the contained iterators.
(As extra fun, a reverse reverse iterator will work with this trait).
I once had to do something very similar with std::vector<T>::operator<, which also blindly called T<T and didn't SFINAE disable it if that wasn't legal.
It may also be the case that a C++ standard library implementation can make a constructor which would not compile not participate in overload resolution. This change could break otherwise well formed programs, but only by things as ridiculous as your static assert (which would flip) or things logically equivalent.
It's just a question of constraints. Or lack thereof. Here's a reduced example with a different trait:
struct X {
template <typename T>
X(T v) : i(v) { }
int i;
};
static_assert(is_constructible_v<X, std::string>); // passes
X x("hello"s); // fails
Whenever people talk about being SFINAE-friendly - this is fundamentally what they're referring to. Ensuring that type traits give the correct answer. Here, X claims to be constructible from anything - but really it's not. is_constructible doesn't actually instantiate the entire construction, it just checks the expression validity - it's just a surface-level check. This is the problem that enable_if and later Concepts are intended to solve.
For libstdc++ specifically, we have:
template<typename _Iter>
_GLIBCXX17_CONSTEXPR
reverse_iterator(const reverse_iterator<_Iter>& __x)
: current(__x.base()) { }
There's no constraint on _Iter here, so is_constructible_v<reverse_iterator<T>, reverse_iterator<U>> is true for all pairs T, U, even if it's not actually constructible. The question uses assignable, but in this case, assignment would go through this constructor template, which is why I'm talking about construction.
Note that this is arguably a libstdc++ bug, and probably an oversight. There's even a comment that this should be constrained:
/**
* A %reverse_iterator across other types can be copied if the
* underlying %iterator can be converted to the type of #c current.
*/
I got very weird problem
for code like this
template <typename T>
struct A{
explicit A(unsigned int size = 0, const T &t = T())
{
}
template <typename InputIterator>
A(InputIterator first, InputIterator last) {
for(;first != last ; ++first)
{
*first; //do something with iterator
}
}
};
when I for example define
A<int> a(10,10);
the second constructor for iterators is used instead of first one.
How then vectors constructors work, when they looks preety the same?
explicit vector (size_type n, const value_type& val = value_type(),
const allocator_type& alloc = allocator_type());
template <class InputIterator>
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
And I can make vector v(10,10) without having any troubles.
PS I got error like this
temp.cpp: In instantiation of ‘A<T>::A(InputIterator, InputIterator) [with = int; T = int]’:
temp.cpp:17:15: required from here
temp.cpp:12:4: error: invalid type argument of unary ‘*’ (have ‘int’)
The reason the compiler chooses the second constructor in case of your A is simple: your 10 is a value of signed type int, while size_type is some unsigned integer type. This means that 10 has to be converted to that unsigned integer type. The need for that conversion is what makes the first constructor to lose the overload resolution to the second constructor (which is an exact match for InputIterator = int). You can work around this problem by doing
A<int> a(10u, 10);
This eliminates the need for int -> unsigned conversion and makes the first constructor win the overload resolution through "non-template is better than template" clause.
Meanwhile, the reason it works differently with std::vector is that the language specification gives special treatment to the constructors of standard sequences. It simply requires that std::vector constructor invocation with two integers of the same type as arguments is somehow "magically" resolved to the first constructor from your quote (i.e. the size-and-initializer constructor). How each specific implementation achieves that is up to the implementation. It can overload the constructor for all integer types. It can use a functionality similar to enable_if. It can even hardcode it into the compiler itself. And so on.
This is how it is stated in C++03, for example
23.1.1 Sequences
9 For every sequence defined in this clause and in clause 21:
— the constructor
template <class InputIterator>
X(InputIterator f, InputIterator l, const Allocator& a = Allocator())
shall have the same effect as:
X(static_cast<typename X::size_type>(f),
static_cast<typename X::value_type>(l), a)
if InputIterator is an integral type
C++11, takes it even further by approaching it from a different angle, although the intent remains the same: it states that if InputIterator does not qualify as input iterator, then the template constructor should be excluded from overload resolution.
So, if you want your class template A to behave the same way std::vector does, you have to deliberately design it that way. You can actually take a peek into the standard library implementation on your platform to see how they do it for std::vector.
Anyway, a low-tech brute-force solution would be to add a dedicated overloaded constructor for int argument
explicit A(unsigned int size = 0, const T &t = T())
{ ... }
explicit A(int size = 0, const T &t = T())
{ ... }
That might, of course, imply that you'll eventually have to add overloads for all integer types.
A better solution, which I already mentioned above, would be to disable the template constructor for integer arguments by using enable_if or similar SFINAE-based technique. For example
template <typename InputIterator>
A(InputIterator first, InputIterator last,
typename std::enable_if<!std::is_integral<InputIterator>::value>::type* = 0)
Do you have C++11 features available to you in your compiler?
When InputIterator isint, the instantiated
A(int first, int last)
is a better match than the instantiated
explicit A(unsigned int size = 0, const int &t = int())
due to the first argument being unsigned. A<int> a((unsigned int)10,10) should call the constructor you expect. You can also use SFINAE to prevent a match unless the constructor really is being passed two iterators to T:
#include <iostream>
using namespace std;
template <typename T>
struct A{
explicit A(unsigned int size = 0, const T &t = T())
{
cout << "size constructor for struct A" << endl;
}
template <class I>
using is_T_iterator = typename enable_if<is_same<typename iterator_traits<I>::value_type, T>::value, T>::type;
template <typename InputIterator>
A(InputIterator first, InputIterator last, is_T_iterator<InputIterator> = 0) {
cout << "iterator constructor for struct A" << endl;
for(;first != last ; ++first)
{
*first; //do something with iterator
}
}
};
int main()
{
A<int>(10,10);
A<int>((int*)0,(int*)0);
//A<int>((char*)0,(char*)0); //<-- would cause compile time error since (char *) doesn't dereference to int
return 0;
}
If the condition that both arguments are iterators to T is too strict, there are looser formulations. For instance, you can guarantee that both arguments are iterators. You could go further (but not quite as far as the example above) and ensure that they "point" to a type that is convertible to T (using std::is_convertible).
That is correct, the templated thing is a better match, so it is selected. The standard library implementation fight hard to steer for a sensible behavior with all the templated members. You may want to look up some implementation code for specializations, if you want to implement a similar collection of your own.
Or you may find a way to dodge the problem.
There was a fine GOTW article with all the cases of function overload selection and some advice to fight it.
Your first argument in A<int>(10, 10) doesn't match your explicit constructor because 10 is signed, so it's using the templatized constructor instead. change it to A<int>(10u, 10) and you may end up with the results you expect.
If you're writing a general library, you may want to make the
extra effort, and use template meta programming to catch all of
the cases. Or simply provide explicit overloads for all of the
integral types. For less generic use, it is usually sufficient
to follow the rule that anytime you provide an overload for any
integral type, you also provide one for int (so you would have
a constructor A::A( int size, T const& initialValue = T() ),
in addition to the ones you already provide).
More generally: you should probably just make the size an
int, and be done with it. The standard library is caught up
in a lot of historical issues, and must use size_t by default,
but in general, unless there is a very strong reason to do
otherwise, the normal integral type in C++ is int; in
addition, the unsigned types in C++ have very strange semantics,
and should be avoided anytime there is any chance of arithmetic
operations occuring.
In C++ reference, the find method is defined as
template <class InputIterator, class T>
InputIterator find (InputIterator first, InputIterator last, const T& val);
However, when I use find method, I use find() method without explicitly describing the InputIterator and T.
For example, I use
std::vector<T> aList
...
std::list<int>::iterator pointer = std::find(aList.begin(), aList.end(), *it);
not this
std::list<int>::iterator pointer = std::find<std::list<int>::iterator, int>(aList.begin(), aList.end(), *it);
How does it work? Why don't I need to specify the types when I use find method?
It's called argument type deduction. From the C++ Standard (C++11 version):
(§14.8.2/1) When a function template specialization is referenced, all of the template arguments shall have values. The values can be explicitly specified or, in some cases, be deduced from the use or obtained from default
template-arguments. [ Example:
void f(Array<dcomplex>& cv, Array<int>& ci) {
sort(cv); // calls sort(Array<dcomplex>&)
sort(ci); // calls sort(Array<int>&)
}
and
void g(double d) {
int i = convert<int>(d); // calls convert<int,double>(double)
int c = convert<char>(d); // calls convert<char,double>(double)
}
— end example ]
Type deduction is only done when arguments are present, i.e. it only works for function templates, not for class templates (not even for the constructor).
Type deduction can cause very complicated ambiguities, especially when multiple template specializations and/or overloaded function definitions are given. In some cases, it won't be possible, and then you must specify some or all of the template arguments explicitly using the angle-bracket syntax.
Because the types are deduced. The compiler can look at the call, and figure out that since you're passing a value aList.begin(), the iterator will have the type that is the return type of aList.begin(). Similarly with using *it - the compiler knows what type *it is, hence can deduce the type being passed in as a function argument.
vector<T> has a constructor that takes the size of the vector, and as far as I know it is explicit, which can be proved by the fact that the following code fails to compile
void f(std::vector<int> v);
int main()
{
f(5);
}
What I cannot understand and am asking you to explain is why the following code compiles
std::vector<std::vector<int>> graph(5, 5);
Not only does it compile, it actually resizes graph to 5 and sets each element to a vector of five zeros, i.e. does the same as would the code I would normally write:
std::vector<std::vector<int>> graph(5, std::vector<int>(5));
How? Why?
Compiler: MSVC10.0
OK, seems it's an MSVC bug (yet another one). If someone can elaborate on the bug in an answer (i.e. summarize the cases where it is reproduced) I would gladly accept it
It is not really a bug. The question is what could go wrong to allow the second piece of code while the first does not compile?
The issue is that while it seems obvious to you what constructor you want to call when you do:
std::vector<std::vector<int>> graph(5, 5);
it is not so clear for the compiler. In particular there are two constructor overloads that can potentially accept the arguments:
vector(size_type,const T& value = T());
template <typename InputIterator>
vector(InputIterator first, InputIterator last);
The first one requires the conversion of 5 to size_type (which is unsigned), while the second is a perfect match, so that will be the one picked up by the compiler...
... but the compiler requires that the second overload, if the deduced type InputIterator is integral behaves as if it was a call to:
vector(static_cast<size_type>(first),static_cast<T>(last))
What the C++03 standard effectively mandates is that the second argument is explicitly converted from the original type int to the destination type std::vector<int>. Because the conversion is explicit you get the error.
The C++11 standard changes the wording to use SFINAE to disable the iterator constructor if the argument is not really an input iterator, so in a C++11 compiler the code should be rejected (which is probably the reason some have claimed this to be a bug).
To me it looks like it's calling this constructor:
template <class InputIterator>
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
I'm not sure where explicit comes into it, because the constructor takes multiple parameters. It's not auto casting from an int to a vector.
This is actually an extension, not a bug.
The constructor being invoked is the one that takes two iterators (but really, the signature will match any two parameters of the same type); it then invokes a specialization for when the two iterators are actually int, which explicitly constructs a value_type using the value of end and populates the vector with begin copies of it.