I'm trying to overload operator[] for an std::map instance, and seriously puzzled by the compilation errors from GCC.
The following example will not compile:
typedef std::map< int*, int > mymap;
namespace std {
template <>
int & mymap::operator[]( const int* & k) {
return begin()->second;
};
};
This one fails with:
error: template-id 'operator[]<>' for 'int& std::map, std::allocator > >::operator[](const int*&)' does not match any template declaration
But if you replace int* with myintp (typedef int* myintp) it will compile just fine.
It's also interesting why template<> and namespace are needed here.
Update:
I oversimplified the example.
It is allowed to add template specializations for any standard library
template to the namespace std only if the declaration depends on a
user-defined type and the specialization satisfies all requirements
for the original template, except where such specializations are
prohibited.
typedef std::map< myclass*, int > mymap;
namespace std {
template <>
int & mymap::operator[]( myclass* const & k) {
return begin()->second;
};
};
Would that example provide a legal and predictable behavior?
From 17.6.4.2.1/2:
The behavior of a C++ program is undefined if it declares
— an
explicit specialization of any member function of a standard library
class template, or
So right there all bets are off and the compiler has no obligation to compile your code (other answers show why the compiler appears to accept the code that uses the typedef, but that still doesn't make it legal).
C++98 is slightly less explicit, in 17.4.3.1/1:
It is undefined for a C++ program to add declarations or definitions
to namespace std or namespaces within namespace std unless otherwise
specified. A program may add template specializations for any standard
library template to namespace std. Such a specialization (complete or
partial) of a standard library template results in undefined behavior
unless the declaration depends on a user-defined name of external
linkage and unless the specialization meets the standard library
requirements for the original template.
Now, it's somewhat unclear if this strictly prohibits specializing a member of a template of std namespace (vs a full or partial specialization of the container template itself) but your particular code is certainly undefined because it doesn't specialize on a user-defined name of external linkage.
The key type is int*.
In order to match
T& operator[]( const Key& key );
you need to use:
int & mymap::operator[]( int* const & k) {
Unfortunately, use of const before the type is confusing. Had the declaration been
T& operator[]( Key const& key );
it would have been easier to come up with the right argument declaration for what you are trying.
Let's analyze your code:
int& mymap::operator[](const int* &k)
{
return begin ()->second;
};
This is wrong since the parameter is a reference to a pointer to a const int, not a reference to a const pointer to an int.
The position of const have confused you, so rewrite it as:
int& mymap::operator[](int *const &k)
{
return begin ()->second;
};
Related
The following code compiles in C++11, C++14, and C++17, but does not compile in C++20. What change to the standard broke this code?
#include <vector>
#include <utility>
template<typename T>
struct bar
{
typename T::reverse_iterator x;
};
struct foo
{
bar<std::vector<std::pair<foo, foo>*>> x;
};
int main()
{
foo f;
}
The error is quite long, but can be summarized as:
template argument must be a complete class
This was always undefined. [res.on.functions]/2.5 says:
In particular, the effects are undefined in the following cases:
[...]
If an incomplete type ([basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.
std::pair does not (and cannot) support incomplete types. You were just relying on order of instantiation to kind of get around that. Something changed in the library that slightly changed the evaluation order, leading to the error. But undefined behavior is undefined - it happened to work before and it happens to not work now.
As to why it's specifically C++20 that is causing this to fail. In C++20, iterators changed to have this new iterator_concept idea. In order to instantiate that, reverse_iterator needs to determine what the concept should be. That looks like this:
#if __cplusplus > 201703L && __cpp_lib_concepts
using iterator_concept
= conditional_t<random_access_iterator<_Iterator>,
random_access_iterator_tag,
bidirectional_iterator_tag>;
using iterator_category
= __detail::__clamp_iter_cat<typename __traits_type::iterator_category,
random_access_iterator_tag>;
#endif
Now, in the process of checking random_access_iterator, the root of iterator concept hierarchy is wonderfully named input_or_output_iterator, specified in [iterator.concept.iterator]:
template<class I>
concept input_or_output_iterator =
requires(I i) {
{ *i } -> can-reference;
} &&
weakly_incrementable<I>;
So, we have to do *i on our iterator type. That's __gnu_cxx::__normal_iterator<std::pair<foo, foo>**, std::vector<std::pair<foo, foo>*> > , in this case. Now, *i triggers ADL - because of course it does. And ADL requires instantiation of all the associated types - because those associated types might have injected friends that could be candidates!
This, in turn, requires instantiating pair<foo, foo> - because, we have to check. And then that ultimately fails in this specific case because instantiating a type requires instantiating all of the type's special member functions, and the way that libstdc++ implements conditional assignment for std::pair is using Eric Fisellier's trick:
_GLIBCXX20_CONSTEXPR pair&
operator=(typename conditional<
__and_<is_copy_assignable<_T1>,
is_copy_assignable<_T2>>::value,
const pair&, const __nonesuch&>::type __p)
{
first = __p.first;
second = __p.second;
return *this;
}
And is_copy_assignable requires complete types and we don't have one.
But really even if pair used concepts to check in this case, that would still involve instantiating the same type traits, so we'd ultimately end up in the same position.
Moral of the story is, undefined behavior is undefined.
I have a function for case insensitive comparison of strings which uses std::lexicographical_compare with custom comparator.
However i would like to be able to compare strings, string_views and const char* between each other, for maximum convenience and efficiency.
So i was thinking: What if i make a template, std::string has begin/end, std::string_view has begin/end, ... but const char* doesn't, not even in a form of non-member function.
So it is ok to define own begin/end overloads like this
namespace std {
const char * begin(const char* str) { return str; }
const char * end(const char* str) { return str + strlen(str); }
}
so that then i can compare everything with everything by
std::lexicographical_compare(std::begin(a), std::end(a), std::begin(b), std::end(b), icomp );
?
If not, how else could i solve my problem?
No, this is not legal, because const char * is not a user-defined type.
The behavior of a C++ program is undefined if it adds declarations or
definitions to namespace std or to a namespace within namespace std
unless otherwise specified. A program may add a template
specialization for any standard library template to namespace std only
if the declaration depends on a user-defined type and the
specialization meets the standard library requirements for the
original template and is not explicitly prohibited
[namespace.std/1]
You can instead declare those in some other namespace, such as ::
const char * begin(const char* str) { return str; }
const char * end(const char* str) { return str + strlen(str); }
And use them with unqualified calls
std::lexicographical_compare(begin(a), end(a), begin(b), end(b), icomp );
Additionally, in C++20, it will be even more restrictive, permitting only class templates specialisations for program-defined types
Unless otherwise specified, the behavior of a C++ program is undefined
if it adds declarations or definitions to namespace std or to a
namespace within namespace std.
Unless explicitly prohibited, a program may add a template
specialization for any standard library class template to namespace
std provided that (a) the added declaration depends on at least one
program-defined type and (b) the specialization meets the standard
library requirements for the original template.
[namespace.std]
Noob here,
I'm trying to compile this segment of code from Bjarne Stroustrup's 'The C++ Programming Language' but CodeBlocks keeps throwing me this error.
The code is about range checking an array held in a vector function.
Here is the code:
#include <iostream>
#include <vector>
#include <array>
using namespace std;
int i = 1000;
template<class T> class Vec : public vector<T>
{
public:
Vec() : vector<T>() { }
T& operator[] (int i) {return at(i); }
const T& operator[] (int i) const {return at(i); }
//The at() operation is a vector subscript operation
//that throws an exception of type out_of_range
//if its argument is out of the vector's range.
};
Vec<Entry> phone_book(1000);
int main()
{
return 0;
}
The errors returned are:
there are no arguments to 'at' that depend on a template parameter, so a declaration of 'at' must be available
note: (if you use '-fpermissive', G++ will accept your code, but allowing the use of an undeclared name is deprecated
In member function 'const T& operator[] (int i) const':
there are no arguments to 'at' that depend on a template parameter, so a declaration of 'at' must be available
'Entry' was not declared in this scope
template argument 1 is invalid
invalid type in declaration before '(' token
Can someone explain this to me?
Also, how would I implement this if I were to not use 'using namespace std;'
Replace at with vector<T>::at or this->at.
Rules for how functions are looked up in templates are tighter now than when C++ was being originally designed.
Now, methods in dependent bases are only looked up if you this->, otherwise it is assumed to be a global function (or a non-dependent base/class local/etc).
This can help avoid nasty surprises in practice, where what you thought was a method call becomes a global one, or a global call becomes a method call. It also allows earlier checking of template method bodies.
In addition to Yakk's answer, another solution would be to add
using vector<T>::at;
to Vec basically adding it to the list of looked up functions.
This way, at() can be used as usual without prefixing it with the base class type or this->.
Consider the following excerpt from the safe bool idiom:
typedef void (Testable::*bool_type)() const;
operator bool_type() const;
Is it possible to declare the conversion function without the typedef? The following does not compile:
operator (void (Testable::*)() const)() const;
Ah, I just remembered the identity meta-function. It is possible to write
operator typename identity<void (Testable::*)() const>::type() const;
with the following definition of identity:
template <typename T>
struct identity
{
typedef T type;
};
You could argue that identity still uses a typedef, but this solution is "good" enough for me.
One case (unrelated to your question) where a typedef is required is when using the
va_arg() macro. Quoting the C99 standard (7.15.1.1):
type* va_arg(va_list ap, type);
...
The parameter type shall be a
type name specified such that the type of a pointer to an object that
has the specified type can be obtained simply by postfixing a * to
type
Answering the "Are there cases where a typedef is absolutely necessary?" from the question title, here is one example of where a typedef is needed:
f(unsigned char()); // compiler error!
typedef unsigned char Byte;
f(Byte()); // fine!
See the results here: http://ideone.com/JPUra
My analysis says that it is not possible without using typedef. The compiler sees ( as the first token and assumes you are overloading () operator, which shouldn't have any arguments (The arguments would come in next set of parenthesis). Putting any set of extra parenthesis wouldn't help either - but would actually confuse the compiler and hence set of more errors.
Most of the STL code is on top of typedefinitions, and we should/must use them!
It seems that the grammar demands using a typedef in your case. A conversion-function-id must be of the form operator conversion-type-id. The conversion-type-id cannot contain parentheses. So you must use typedef when converting to a pointer-to-function type, or to a pointer-to-member-function type.
In C++11, you can do it like this (gcc 4.5.2):
operator decltype((void (Testable::*)() const)(0))() const ;
I'm not saying it's pretty...
A typedef is not a macro your second example is not equivalent to the first. In the first case your typedef is defining a functor then using that type in a cast operator of the functor type. In the second the operator is using bad syntax as there is no operator specified because there is no type. I'm not sure how to write it but there is a way usually.
Typedefs aren't really necessary except for making human readable code in TMP and even then it depends on what kind of human you are.
Since I can't come up with the alternative syntax maybe typedefs are necessary in some cases. I just thought of another one possibly. Say you had a template with specializations which contained a static method with a return type like below:
template <typename T>
struct WhateverHandler
{
typedef T rType;
static rType Whatever() { return rType(); }
};
template <>
struct WhateverHandler<std::string>
{
typedef std::string rType;
static rType Whatever() { return rType(); }
};
I think in this case also you would need the typedef in order to call the static method regardless of specialization as otherwise the method could confuse the compiler because the return types would differ but it wouldn't be a proper overload.
template <typename T>
struct WhateverUser
{
typename WhateverHandler<T>::rType DoWhatever()
{
return WhateverHandler<T>::template Whatever();
}
};
I just ran across this issue, with clang++:
foo.cpp:17:8: error: must use a typedef to declare a conversion to 'void (*(int))()'
and there's a C++11 STL template which covers the identity<T> functionality:
#include <type_traits>
…
struct foo {
void bar( ) const { }
operator std::common_type<void(foo::*)( )const>::type( ) { return &foo::bar; }
};
When compiling :
#include <vector>
template<class T> class foo {
void bar() {
std::vector<T> x;
std::vector<T>::iterator i = x.begin();
}
};
int main() {
return 0;
}
I get :
# g++ ~test.cpp
test.cpp: In member function `void foo<T>::bar()':
test.cpp:7: error: expected `;' before "i"
Shouldn't this work?
g++ version 3.4.3 on RHEL.
You can, but you need to tell it that iterator there is a type (it doesn't know, because in general it can depend on T - as vector is a template type, and could in theory have specializations for some T where iterator is a function or something else). So, you have to use typename to indicate that it is always a type:
typename std::vector<T>::iterator i = x.begin();
This should do:
template<class T> class foo {
void bar() {
std::vector<T> x;
typename std::vector<T>::iterator i = x.begin();
}
};
I'll quote the IBM C++ compiler manual:
The typename keyword (C++ only) Use
the keyword typename if you have a
qualified name that refers to a type
and depends on a template parameter.
Only use the keyword typename in
template declarations and definitions.
The following example illustrates the
use of the keyword typename:
template<class T> class A
{
T::x(y);
typedef char C;
A::C d;
}
The statement T::x(y) is ambiguous. It
could be a call to function x() with a
nonlocal argument y, or it could be a
declaration of variable y with type
T::x. C++ will interpret this
statement as a function call. In order
for the compiler to interpret this
statement as a declaration, you would
add the keyword typename to the
beginning of it. The statement A::C d;
is ill-formed. The class A also refers
to A and thus depends on a template
parameter. You must add the keyword
typename to the beginning of this
declaration:
typename A::C d; You can also use
the keyword typename in place of the
keyword class in template parameter
declarations.
In case it isn't clear what the others mean by the compiler not knowing that it's a type: at the point where the compiler parses template foo, it doesn't know that you won't later do:
namespace std {
template<>
class vector<int> {
int iterator(void);
};
}
And then instantiate foo<int>. Then vector<T>::iterator would be a function, not a type, and the relevant line in foo should fail to parse. To work without help, compilers would have to hold off parsing foo until it's instantiated, and they've found the right class to determine whether 'iterator' is a type expression or a value expression. I suspect this could lead to circular dependencies, but would certainly be especially painful to implement. So the standard says that an expression in a template which is dependent on a parameter is assumed not to be a type unless stated to be. There are two (I think) ways to state it to be a type, which are (1) use it as a base class, and (2) qualify it with typename.
OK, so in this example you're actually not allowed to specialize std::vector. And vector actually has more template parameters than just the one I've used. So in your example, the compiler could in theory assume more than it does. But the standard doesn't make special provision for the language to rely on knowledge of what templates are in namespace std, because (1) the intention is that implementations can implement namespace std in normal headers treated the same as any other headers by the compiler, and (2) C++ is supposed to be designed as language+libraries, not as "language with special-case syntax for libraries". In fact, (1) and (2) are sort of the same requirement.