Where to add an overloaded operator for the tr1::array? - c++

Since I need to add an operator& for the std::tr1::array<bool, N> I wrote the following lines
template<std::size_t N>
std::tr1::array<bool, N>
operator& (const std::tr1::array<bool, N>& a,
const std::tr1::array<bool, N>& b)
{
std::tr1::array<bool, N> result;
std::transform(a.begin(), a.end(), b.begin(), result.begin(),
std::logical_and<bool>());
return result;
}
Now I don't know in which namespace I've to put this function. I considered the std namespace as a restricted area. Only total specialization and overloaded function templates are allowed to be added by the user. Putting it into the global namespace isn't "allowed" either in order to prevent pollution of the global namespace and clashes with other declarations. And finally putting this function into the namespace of the project doesn't work since the compiler won't find it there.
What had I best do? I don't want to write a new array class putted into the project namespace. Because in this case the compiler would find the right namespace via argument dependent name lookup. Or is this the only possible way because writing a new operator for existing classes means extending their interfaces and this isn't allowed either for standard classes?

I fully support GMan and sbk who told you to use a named function instead of an operator. Contrary to popular believe, overloading operators is always almost wrong, because it almost never adds clarity to the code. There are surprisingly few exceptions. Among them are the stream input and output operators as well as the arithmetical operators should you implement a number-like type. (And just how likely is that outside of a book teaching you operator overloading?) Note that some people frown upon the std lib overloading + (and +=, of course) for std::string for the same reason (and others, like that a+b==b+a holds for numbers, but not for strings) - and IMO they do have a point.
Anyway if one wanted to do this despite all advice:
When you try to invoke the operator, the compiler tries to find it in the namespace it was invoked in, all enclosing namespaces, and the namespaces of all the arguments. (The latter is called argument-dependent lookup or Koenig lookup.) The namespace of the argument is std, which you must not add an overload to. So that leaves the namespace the operator is invoked in and its enclosing namespaces - including the global namespace, which encloses all others - to put the operator in.
So if you want to implement it despite all warnings, put it in the namespace where it is used in. If it is used in several namespaces, put it into the one that encloses all these. If that's the global namespace, so be it.
Oh, and did I mention you should not implement this as an overloaded operator?

AFAIK, you can add the overload to any namespace (except std, because you are not allowed to add new functions to it), and you'd make it visible with a using declaration. This is how, for example, boost's assign library works (pay attention to the introductory snippets; for a precedent in the standard library, check out rel_ops). Also note that you can restrict the scope of using declarations, so as to avoid global pollution.
Example, using boost::array because of an old compiler:
#include <boost/array.hpp>
#include <functional>
#include <algorithm>
using boost::array;
namespace bitarray_operators
{
template <size_t N>
array<bool, N> operator& (const array<bool, N>& a, const array<bool, N>& b)
{
array<bool, N> result;
std::transform(a.begin(), a.end(), b.begin(), result.begin(), std::logical_and<bool>());
return result;
}
}
int main()
{
using namespace bitarray_operators; //<-- this makes it possible to find the & operator
array<bool, 100> a, b, c;
c = a & b;
}
I concur that overloading this operator might be somewhat questionable. It's also unclear to me why you don't use std::bitset which overloads this operator with potentially better performance possible because of a more compact internal representation (each bool taking one bit, instead of at least the size of a char).

Related

Does std::(customization point) invoke the most appropriate overload?

Since C++20, the concept of customization point is introduced in [namespace.std]/7:
Other than in namespace std or in a namespace within namespace std, a program may provide an overload for any library function template designated as a customization point, provided that (a) the overload's declaration depends on at least one user-defined type and (b) the overload meets the standard library requirements for the customization point. [ Note: This permits a (qualified or unqualified) call to the customization point to invoke the most appropriate overload for the given arguments. — end note ]
Does the note part (note the emphasized word "qualified") mean that std::f will automatically invoke the most appropriate overload for f if std::f is a customization point?
A real example is std::swap, which is a designated customization point. Does this mean since C++20, we can write std::swap(a, b) directly instead of using std::swap; swap(a, b);?
A real example is std::swap, which is a designated customization point. Does this mean since C++20, we can write std::swap(a, b) directly instead of using std::swap; swap(a, b);?
No. std::swap itself did not gain any powers. It's still just a function template, so if you call it directly, you're... calling it directly. No ADL or anything.
The point of this is to say how customization points should be opted into. That is, you write:
namespace N { // not std
void swap(Foo&, Foo&);
}
Not:
namespace std {
void swap(N::Foo&, N::Foo&);
}
Nor:
namespace std {
template <>
void swap(N::Foo&, N::Foo&);
}
However, C++20 does introduce a lot of new things called customization point objects which you can use directly do this kind of thing. The CPO for swap is spelled std::ranges::swap (and likewise there are CPOs for all the useful ranges things... ranges::begin, ranges::end, etc.).

Overloading std::begin() and std::end() for non-arrays

Let's assume I have the following Data class:
struct Data {
char foo[8];
char bar;
};
and the following function, my_algorithm, which takes a pair of char * (similar to an STL algorithm):
void my_algorithm(char *first, char *last);
For Data's foo data member, instead of calling my_algorithm() like this:
Data data;
my_algorithm(data.foo, data.foo + 8);
I can use the std::begin() and std::end() convenience function templates:
my_algorithm(std::begin(data.foo), std::end(data.foo));
I would like to achieve something similar to Data's bar data member. That is, instead of writing:
my_algorithm(&data.bar, &data.bar + 1);
I would like to write something like:
my_algorithm(begin(data.bar), end(data.bar));
Therefore, I've defined the two following ordinary (non-template) functions for this case:
char* begin(char& c) { return &c; }
char* end(char& c) { return &c + 1; }
So that I would be able to write code like the following:
Data data;
using std::begin;
using std::end;
my_algorithm(begin(data.foo), end(data.foo)); // ok - std::begin()/std::end()
my_algorithm(begin(data.bar), end(data.bar)); // Error!!!
With the using declarations above I would have expected std::begin()/std::end() and ::begin()/::end() to be in the same overload set, respectively. Since the functions ::begin() and ::end() are a perfect match for the latter call and they are not templates, I would have expected the last call to my_algorithm() to match them. However, the ordinary functions are not considered at all. As a result the compilation fails, because std::begin() and std::end() are not matches for the call.
Basically, the latter call acts as if I had written instead:
my_algorithm(begin<>(data.bar), end<>(data.bar));
That is, only the function templates (i.e., std::begin()/std::end()) are considered by the overload resolution process, not the ordinary functions (i.e., not ::begin()/::end()).
It only works as expected, if I fully qualify the calls to ::begin()/::end():
my_algorithm(::begin(data.bar), ::end(data.bar));
What am I missing here?
Let's get a complete, reproducible example:
#include <iterator>
char* begin(char& c) { return &c; }
char* end(char& c) { return &c + 1; }
namespace ns {
void my_algorithm(char *first, char *last);
void my_function() {
using std::begin;
using std::end;
char c = '0';
my_algorithm(begin(c), end(c));
}
}
When you make the unqualified call to begin(c) and end(c), the compiler goes through the process of unqualified name lookup (described on the Argument-dependent lookup page of cppreference).
For regular unqualified name lookup, the process is roughly to start at the namespace you are currently in—::ns in this case—and only move out a namespace if you don't find the specific name.
If a function call is unqualified, as it is here with begin(c) and end(c), argument dependent lookup can occur, which finds free functions declared in the same namespace as the types of the functions' arguments, through the process of extending the overload set by finding "associated namespaces."
In this case, however, char is a fundamental type, so argument dependent lookup doesn't allow us to find the global ::begin and ::end functions.
For arguments of fundamental type, the associated set of namespaces and classes is empty
cppreference: argument dependent lookup
Instead, as we already have using std::begin; using std::end;, the compiler already sees possible functions for begin(...) and end(...)—namely those defined in namespace ::std—without having to move out a namespace from ::ns to ::. Thus, the compiler uses those functions, and compilation fails.
It's worth noting that the using std::begin; using std::end; also block the compiler from finding the custom ::begin and ::end even if you were to place them inside ::ns.
What you can do instead is write your own begin and end:
#include <iterator>
namespace ns {
char* begin(char& c) { return &c; }
char* end(char& c) { return &c + 1; }
template <typename T>
auto begin(T&& t) {
using std::begin;
// Not unbounded recursion if there's no `std::begin(t)`
// or ADL `begin(t)`, for the same reason that our
// char* begin(char& c); overload isn't found with
// using std::begin; begin(c);
return begin(t);
}
template <typename T>
auto end(T&& t) {
using std::end;
return end(t);
}
void my_algorithm(char *first, char *last);
void my_function() {
char c = '0';
my_algorithm(ns::begin(c), ns::end(c));
}
}
The title of question is "Overloading std::begin()". Overloading is possible only within the same scope. That is you can't overload names from different scopes. In another scope we can only make efforts to help lookup name. Essentially, here "using std::begin" declaration hides ::begin in question's code. See S.Lippman for reference:
functions that are members of two distinct namespaces do not overload one another.
Scope of a using Declaration. Names introduced in a using declaration obey normal scope rules.
Entities with the same name defined in an outer scope are hidden.
As soon as parameter is char and char is fundamental type - argument dependent lookup should not be taken into consideration - as mentioned in comments - there is no associated namespace with fundamental types.
Again, the question was: "What am I missing?" - therefore the answer is focused only on reasons - recommendations may be too broad.

redefine < operator to use in stl algorithms for strings

Is it possible to redefine operator < for strings without modifying std namespace, to make this operator use in standard algorithms?
For example, I can write:
namespace std
{
bool operator <(const std::string & rhs, const std::string & lhs)
{
std::cout << "lol";
return false;
}
}
int main()
{
std::vector<std::string> lol = { "a", "b", "ba", "aa" };
std::sort(lol.begin(), lol.end());
}
and "lol" will be printed several times. But if I move operator < outside from std namespace, default operator < will be used and nothing will be printed. Is it possible to make std::sort using custom operator < without including it to std namespace?
Yes I know, I can pass another comparator to std::sort but it's interesting for me if I could do what I asked and how?
Also am I correct, that it's correct to add such template specialization to std namespace?
Update: This is not practical question, I just want to know how can I do that if it's possible.
No, it is not. Adding a function to the standard namespace is undefined behavior. [namespace.std]/1 states:
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.
If you want to change how std::sort sorts then you can provide a lambda and define what you want
std::sort(std::begin(foo), std::end(foo), [](const auto& lhs, const auto& rhs) { /* your code here */ });
Is it possible to redefine operator < for strings without modifiying std namespace
You can define the overload in another namespace, sure. But as you have found out, it will not be found by overload resolution unless explicitly qualified.
Is it possible to make std::sort using custom operator < without including it to std namespace?
Yes, and you already seem to know how:
Yes I know, I can pass another comparator to std::sort
This is exactly what the comparator argument is for.
Also am I correct, that it's corect to add such template specialization to std namespace?
That is not a template specialization; It is a function definition and you may not add function definitions to std namespace - or else the behaviour is undefined. You would be allowed to add template specializations, but only if at least one type argument is a user defined type.

Rationale for Koenig lookup

What's the rationale of Koenig lookup?
Cannot avoid thinking of it like something that makes your code a lot harder to read and more instable.
Couldn't they define Koenig lookup so that it only work for specific cases (ie: non-member operators) or when explicitly required?
The original motivation, IIRC, was to be able to write
std::cout << 42;
without having to qualify std::operator<<(std::ostream&, int) explicitely.
If you want to disable argument dependant lookup, you can explicitely qualify the function name, ie. use std::swap instead of swap to prevent swap to be looked up in whatever namespace its arguments would live.
ADL can also be used with SFINAE to test at compile time whether some function is defined for a particular type (I'll let you work this out as an exercise, there is at least one question about this on Stackoverflow).
The strongest use case for ADL is for cases like this.
namespace A
{
struct S {};
S operator+( const S&, const S& );
}
namespace B
{
A::S test()
{
A::S a, b;
return a + b;
}
}
It is also useful for selecting the correct swap function in generic code so it shouldn't only apply to operator functions. It is already a fairly complex part of the standard, making rules that prevented it from working in some cases would add further complexity, what would be the gain?
I can't think of any neat way of asking for it explicitly that would be significantly less verbose than calling a function in a different namespace directly and would, in any case, make expressions more complex.
We're you thinking something like: return [[ use_adl ]] (a + b); vs. return A::operator+( a, b ); ?

What are the pitfalls of ADL?

Some time ago I read an article that explained several pitfalls of argument dependent lookup, but I cannot find it anymore. It was about gaining access to things that you should not have access to or something like that. So I thought I'd ask here: what are the pitfalls of ADL?
There is a huge problem with argument-dependent lookup. Consider, for example, the following utility:
#include <iostream>
namespace utility
{
template <typename T>
void print(T x)
{
std::cout << x << std::endl;
}
template <typename T>
void print_n(T x, unsigned n)
{
for (unsigned i = 0; i < n; ++i)
print(x);
}
}
It's simple enough, right? We can call print_n() and pass it any object and it will call print to print the object n times.
Actually, it turns out that if we only look at this code, we have absolutely no idea what function will be called by print_n. It might be the print function template given here, but it might not be. Why? Argument-dependent lookup.
As an example, let's say you have written a class to represent a unicorn. For some reason, you've also defined a function named print (what a coincidence!) that just causes the program to crash by writing to a dereferenced null pointer (who knows why you did this; that's not important):
namespace my_stuff
{
struct unicorn { /* unicorn stuff goes here */ };
std::ostream& operator<<(std::ostream& os, unicorn x) { return os; }
// Don't ever call this! It just crashes! I don't know why I wrote it!
void print(unicorn) { *(int*)0 = 42; }
}
Next, you write a little program that creates a unicorn and prints it four times:
int main()
{
my_stuff::unicorn x;
utility::print_n(x, 4);
}
You compile this program, run it, and... it crashes. "What?! No way," you say: "I just called print_n, which calls the print function to print the unicorn four times!" Yes, that's true, but it hasn't called the print function you expected it to call. It's called my_stuff::print.
Why is my_stuff::print selected? During name lookup, the compiler sees that the argument to the call to print is of type unicorn, which is a class type that is declared in the namespace my_stuff.
Because of argument-dependent lookup, the compiler includes this namespace in its search for candidate functions named print. It finds my_stuff::print, which is then selected as the best viable candidate during overload resolution: no conversion is required to call either of the candidate print functions and nontemplate functions are preferred to function templates, so the nontemplate function my_stuff::print is the best match.
(If you don't believe this, you can compile the code in this question as-is and see ADL in action.)
Yes, argument-dependent lookup is an important feature of C++. It is essentially required to achieve the desired behavior of some language features like overloaded operators (consider the streams library). That said, it's also very, very flawed and can lead to really ugly problems. There have been several proposals to fix argument-dependent lookup, but none of them have been accepted by the C++ standards committee.
The accepted answer is simply wrong - this is not a bug of ADL. It shows an careless anti-pattern to use function calls in daily coding - ignorance of dependent names and relying on unqualified function names blindly.
In short, if you are using unqualified name in the postfix-expression of a function call, you should have acknowledged that you have granted the ability that the function can be "overridden" elsewhere (yes, this is a kind of static polymorphism). Thus, the spelling of the unqualified name of a function in C++ is exactly a part of the interface.
In the case of the accepted answer, if the print_n really need ADL print (i.e. allowing it to be overridden), it should have been documented with the use of unqualified print as an explicit notice, thus clients would receive a contract that print should be carefully declared and the misbehavior would be all of the responsibility of my_stuff. Otherwise, it is a bug of print_n. The fix is simple: qualify print with prefix utility::. This is indeed a bug of print_n, but hardly a bug of the ADL rules in the language.
However, there do exist unwanted things in the language specification, and technically, not only one. They are realized more than 10 years, but nothing in the language is fixed yet. They are missed by the accepted answer (except that the last paragraph is solely correct till now). See this paper for details.
I can append one real case against the name lookup nasty. I was implementing is_nothrow_swappable where __cplusplus < 201703L. I found it impossible to rely on ADL to implementing such feature once I have a declared swap function template in my namespace. Such swap would always found together with std::swap introduced by a idiomatic using std::swap; to use ADL under the ADL rules, and then there would come ambiguity of swap where the swap template (which would instantiate is_nothrow_swappable to get the proper noexcept-specification) is called. Combined with 2-phase lookup rules, the order of declarations does not count, once the library header containing the swap template is included. So, unless I overload all my library types with specialized swap function (to supress any candidate generic templates swap being matched by overloading resolution after ADL), I cannot declare the template. Ironically, the swap template declared in my namespace is exactly to utilize ADL (consider boost::swap) and it is one of the most significant direct client of is_nothrow_swappable in my library (BTW, boost::swap does not respects the exception specification). This perfectly beat my purpose up, sigh...
#include <type_traits>
#include <utility>
#include <memory>
#include <iterator>
namespace my
{
#define USE_MY_SWAP_TEMPLATE true
#define HEY_I_HAVE_SWAP_IN_MY_LIBRARY_EVERYWHERE false
namespace details
{
using ::std::swap;
template<typename T>
struct is_nothrow_swappable
: std::integral_constant<bool, noexcept(swap(::std::declval<T&>(), ::std::declval<T&>()))>
{};
} // namespace details
using details::is_nothrow_swappable;
#if USE_MY_SWAP_TEMPLATE
template<typename T>
void
swap(T& x, T& y) noexcept(is_nothrow_swappable<T>::value)
{
// XXX: Nasty but clever hack?
std::iter_swap(std::addressof(x), std::addressof(y));
}
#endif
class C
{};
// Why I declared 'swap' above if I can accept to declare 'swap' for EVERY type in my library?
#if !USE_MY_SWAP_TEMPLATE || HEY_I_HAVE_SWAP_IN_MY_LIBRARY_EVERYWHERE
void
swap(C&, C&) noexcept
{}
#endif
} // namespace my
int
main()
{
my::C a, b;
#if USE_MY_SWAP_TEMPLATE
my::swap(a, b); // Even no ADL here...
#else
using std::swap; // This merely works, but repeating this EVERYWHERE is not attractive at all... and error-prone.
swap(a, b); // ADL rocks?
#endif
}
Try https://wandbox.org/permlink/4pcqdx0yYnhhrASi and turn USE_MY_SWAP_TEMPLATE to true to see the ambiguity.
Update 2018-11-05:
Aha, I am bitten by ADL this morning again. This time it even has nothing to do with function calls!
Today I am finishing the work of porting ISO C++17 std::polymorphic_allocator to my codebase. Since some container class templates have been introduced long ago in my code (like this), this time I just replace the declarations with alias templates like:
namespace pmr = ystdex::pmr;
template<typename _tKey, typename _tMapped, typename _fComp
= ystdex::less<_tKey>, class _tAlloc
= pmr::polymorphic_allocator<std::pair<const _tKey, _tMapped>>>
using multimap = std::multimap<_tKey, _tMapped, _fComp, _tAlloc>;
... so it can use my implementation of polymorphic_allocator by default. (Disclaimer: it has some known bugs. Fixes of the bugs would be committed in a few days.)
But it suddenly does not work, with hundreds of lines of cryptic error messages...
The error begins from this line. It roughly complains that the declared BaseType is not a base of the enclosing class MessageQueue. That seems very strange because the alias is declared with exactly the same tokens to those in the base-specifier-list of the class definition, and I am sure nothing of them can be macro-expanded. So why?
The answer is... ADL sucks. The line inroducing BaseType is hard-coded with a std name as a template argument, so the template would be looked up per ADL rules in the class scope. Thus, it finds std::multimap, which differs to the result of lookup in as the actual base class declared in the enclosing namespace scope. Since std::multimap uses std::allocator instance as the default template argument, BaseType is not the same type to the actual base class which have an instance of polymorphic_allocator, even multimap declared in the enclosing namespace is redirected to std::multimap. By adding the enclosing qualification as the prefix right to the =, the bug is fixed.
I'd admit I am lucky enough. The error messages are heading the problem to this line. There are only 2 similar problems and the other is without any explicit std (where string is my own one being adapted to ISO C++17's string_view change, not std one in pre-C++17 modes). I would not figure out the bug is about ADL so quickly.