Specialization of 'template<class _Tp> struct std::less' in different namespace
I am specializing the 'less' (predicate) for a data type.
The code looks like this:
template<>
struct std::less<DateTimeKey>
{
bool operator()(const DateTimeKey& k1, const DateTimeKey& k2) const
{
// Some code ...
}
};
When compiling (g++ 4.4.1 on Ubuntu 9.10), I get the error:
Specialization of 'template struct std::less' in different namespace
I did some research and found that there was a 'workaround' which involved wrapping the specialization in a std namespace - i.e. changing the code to:
namespace std {
template<>
struct less<DateTimeKey>
{
bool operator()(const DateTimeKey& k1, const DateTimeKey& k2) const
{
// Some code ...
}
};
}
which indeed, shuts the compiler up. However, that solution was from a post 5 years old (By the 'great' Victor Bazarof no less [pun unintended]). Is this fix still the way to go, or is there a better way of resolving this, or is the "old way" still valid?
This is still the way to do it. Unfortunately you cannot declare or define functions within a namespace like you would do with a class: you need to actually wrap them in a namespace block.
If you need to specialize a standard algorithm, you can do so in the std namespace. It is the only thing that you are allowed to do inside that namespace according to the standard.
[lib.reserved.names]/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, the question is whether you actually want to specialize std::less
. Note that std::less
will call the comparison operator defined for your type, so you can provide that operation instead of specializing the template.
The problem with specializing std::less
for your particular type is that it will cause confusion if you provide a different operation than the one performed by operator<
for your type. If they perform the same operation, just leave the default std::less
definition without specialization.
If you do not want to provide the comparison operator, but still want to use the type in associative containers or with algorithms that require a comparator, you can provide an external comparison functor by other name that will not confuse other readers (and yourself somewhere in the future).
Why are you even doing this?
std::less
exists for two purposes only:
- to give a name to operator <, allowing it to be passed as a functor
- to explicitly allow comparing two pointers that aren't in the same array (which is technically illegal if done with raw pointers)
There's no reason for a user to overload it - either overload operator<
or use a custom comparator function.
There are std algorithms that can be sensibly specialized - std::swap
is a good example - and to do so you do need to declare the specialization inside namespace std.
Even though the question has been answered by others with answers on how to specialize std::less
(by wrap them in a namespace block) and the right way to do it ( to overload operator <
).
However, C++
now allows (in C++11
) to speciliaze the way you did in your first example.
An explicit specialization shall be declared in a namespace enclosing the specialized template. An explicit specialization whose declarator-id is not qualified shall be declared in the nearest enclosing namespace of the template, or, if the namespace is inline (7.3.1), any namespace from its enclosing namespace set. Such a declaration may also be a definition. If the declaration is not a definition, the specialization may be defined later (7.3.1.2).
I tried the following code with g++ (8.3.0)
on my Ubuntu
machine.
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
class myType {
public:
myType(int in): i_(in) { }
int i_;
};
template <>
struct std::less<myType> {
bool operator()(const myType& a, const myType& b) const
{
return a.i_ < b.i_;
}
};
int main(int argc, char *argv[])
{
std::map<myType, std::string> vector = { { 1, "1"}, { 2, "2"}, { 3, "3"}, { 0, "0" } };
for (auto& i: vector)
std::cout << i.first.i_ << std::endl;
return 0;
}
The above code was compiled with
g++ --std=c++11 compare.cpp -Wall