Is it discouraged to overload STL functions? - c++

I was wondering if it is discouraged to overload an STL function, and if so, why?
I ran across an issue yesterday, where I found that std::fpclassify doesn't have an integral overload on microsoft compilers (https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fpclassify?view=msvc-170) as it does for other compilers (see (4) in https://en.cppreference.com/w/cpp/numeric/math/fpclassify).
I ran across this issue when I tried to compile
T var; // T can be an integral type
std::isnan(var); //
using microsoft C++ compiler.
I already have a working solution to solve this issue that didn't involve overloading std::fpclassify, but I did consider maybe just writing an overload for std::fpclassify myself, but it seems it would have gotten hairy because the code might be compiled using non-microsoft compilers, in which case, we would already have the integral overload defined.

Is it discouraged to overload STL functions?
You aren't allowed to add any overloads into the std namespace. It's fine to add overloads into custom namespaces.
and if so, why?
Because the standard says so. Probably so that your program won't break when the standard (implementation) changes.

No. There is no issue as long as you are not trying to place your custom overload inside the namespace std, because that is undefined except in some cases.
Consider std::swap where you are even encouraged to provide an overload when swapping two objects should do something that std::swap cannot do:
#include <iostream>
#include <algorithm>
namespace X {
struct foo {
int value;
bool operator<(const foo& other) { return value < other.value; }
};
void swap(foo& a, foo& b) noexcept {
std::cout << "swap\n";
std::swap(a.value,b.value);
}
}
int main() {
std::vector<X::foo> v{{2},{1}};
std::iter_swap(v.begin(),v.begin()+1); // calls X::swap
std::cout << v[0].value << " " << v[1].value << "\n";
std::vector<int> w{2,1};
std::iter_swap(w.begin(),w.begin()+1); // calls std::swap
std::cout << w[0] << " " << w[1];
}
Output:
swap
1 2
1 2
This relies on ADL, which means it wont work for fundamental types or types from std. Only if you wrap them inside a custom class inside some namespace like above you can use it for int and the like.

Related

put_time into stream weird behavior

It was my understanding that overloaded operators are translated by the compiler into method calls, for example :
stream << "test";
Is the same as:
stream.operator<<("test");
If that is indeed the case, then why does this lead into a compilation error:
#include <iostream>
#include <iomanip>
#include <ctime>
int main()
{
std::ostream s {std::cout.rdbuf()};
auto t = std::time(nullptr);
auto tm = *std::localtime(&t);
s.operator<<(std::put_time(&tm, "%H:%M:%S"));//doesn't work
return 0;
}
While changing the doesn't work line to:
s << std::put_time(&tm, "%H:%M:%S");
Makes the code compile.
Both GCC and MSVC 2017 on C++14 seem to produce an error. Is this a compiler bug or are those two statements not equal?
Here's your code, but reduced:
struct X { friend void operator+(X, X); };
int main() {
X var;
var + var; // ok
var.operator+(var); // fail
}
The thing is, by explicitly calling the operator<<, you are relying on the fact that it is indeed an operator of the class (like T& T::operator<<(const U&);) and not an operator defined outside the class (because if the operator is not a member, you can't write var.operator<<(/*...*/)).
If you write var << something, then in addition to the operators defined in the class, lookup (called ADL) finds the operators defined outside of the class as well.
The operator can be defined outside of the class, because std::put_time's return type is specified to ([ext.manip]:
An object of unspecified type such that if out is an object of type basic_­ostream<charT, traits> then the expression out << put_­time(tmb, fmt) behaves as if it called f(out, tmb, fmt), where the function f is defined as: [...]
Because the requirement is of the form var << var2, nothing prohibits standard library implementations to define it outside of the class so that it is found using ADL.

Why does unary operator & not require a complete type?

The following code compiles fine with both gcc 7.2.0 and clang 6.0.0.
#include <iostream>
struct stru;
void func(stru& s) {
std::cout << &s << std::endl;
}
int main() {
}
I'm wondering how this is OK. What if stru has overloaded operator&()? The compiler should not be able to tell with simply a forward declaration like struct stru. In my opinion, only std::addressof(s) is OK with an incomplete type.
What if stru has overloaded operator&()?
Then it is unspecified whether the overload will be called (See Oliv's comment for standard quote).
How could unary operator & does not require a complete type?
That's how the standard has defined the language. The built-in address-of operator doesn't need to know the definition of the type, since that has no effect on where to get the address of the object.
One consideration for why it is a good thing: Compatibility with C.

Does MISRA C++ Rule 14-5-1: (Do not declare a generic function in an associated namespace) apply for std::operator<<?

MISRA C++ 2008, Rule 14-5-1 states that "A non-member generic function shall only be declared in a namespace that is not an associated namespace".
Considering the case of (e.g) overloading the operator<<(), I wonder if this is not the very case to avoid with the MISRA rule. Example:
#include <iostream>
std::ostream& operator<<(std::ostream &s, int x){
s << "my operator<<" << std::endl;
return s;
}
int main() {
std::cout << 5L << std::endl;
return 0;
}
This results in not calling my output operator, but rather the STL output operator. So: is this an example of MISRA rule 14-5-1 violation where ADL picks the unintended function?
As I understand, that rule is to avoid that template (free) functions are used with ADL.
operator<<() doesn't apply, except maybe the following (because of T)
template<class charT, class traits, class T>
basic_ostream<charT,traits>&
operator<< (basic_ostream<charT,traits>&& os, const T& val);
but anyway, operator are mostly useless without ADL, or else we would have to import them before there usage (as we should do for UDL as operator ""s)
Better example would be std::begin which broke the rule:
std::vector<int> v;
begin(v); // -> std::begin(v)
I don't understand which errors that rule try to prevent.

What Extension In Visual Studio 2017 Disambiguates "bool" vs "std::function" When Passing In A Lambda?

The following compiles in Visual Studio 2017 with the MSVC compiler, but fails to compile in GCC or Clang.
#include <iostream>
#include <functional>
void functionTest(std::function<void()>) {
std::cout << "F\n";
}
void functionTest(bool) {
std::cout << "B\n";
}
int main() {
functionTest([](){ std::cout << "wut"; });
}
To fix this we can make use of enable_if like so:
#include <iostream>
#include <functional>
void functionTest(std::function<void()>) {
std::cout << "F\n";
}
template<typename BOOL_TYPE, typename = typename std::enable_if<std::is_same<bool, BOOL_TYPE>::value>::type>
void functionTest(BOOL_TYPE) {
std::cout << "B\n";
}
int main() {
functionTest([](){ std::cout << "wut"; });
}
Or I can disambiguate by introducing a user type instead of a bool (this is what you will need to do in the case of a constructor having the ambiguity problem):
#include <iostream>
#include <functional>
void functionTest(std::function<void()>) {
std::cout << "F\n";
}
enum class DescriptiveTypeName {False, True};
void functionTest(DescriptiveTypeName) {
std::cout << "B\n";
}
int main() {
functionTest([](){ std::cout << "wut"; });
}
The issue I'm having here is that I have a non-trivial sized game project and am trying to compile in Xcode for iOS. As far as I can tell I cannot get the same behaviour Visual Studio exhibits across all compilers (this would be nice). So as a result I am trying to edit my project to make it more conformant to the standard.
In an effort to do this in Visual Studio since it is my primary work environment I would like to know which non-standard extension is being used and how to disable it if at all possible. I can attempt to do this in Xcode, but for this particular problem I've found quite a few ambiguity issues and it only gives me a handful at a time.
As an additional curiosity I'm wondering if this ambiguous case has any standard proposal to address it or if Visual Studio is just going completely rogue in this case.
This is caused by the interaction of two things:
MSVC's captureless lambda has many conversion functions to function pointer - one per calling convention. As a result, converting a captureless lambda to bool via a function pointer is ambiguous in MSVC.
Instead of treating such an ambiguous conversion as being an ambiguous conversion sequence, as the standard requires, MSVC treats it as being no conversion sequence at all, which in turn means that the bool overload is not viable. That leaves the function overload as the only viable one.
The simplest way to disable the conversion to bool is to give the lambda a capture, or a capture-default - even if it doesn't end up capturing anything, having a capture-default is sufficient to disable the conversion to function pointer.
The lambda->bool conversion is actually lambda->function pointer->bool. As one of these doesn't qualify as a "user defined conversion", the double-conversion is considered.
In MSVC, lambda has multiple lambda->function pointer conversions, one per calling convention. This disagrees with the standard, where function pointers do not have calling convention types attached to them.
In any case, this should make the lambda->function pointer->bool conversion ambiguous (and trigger an error), but instead MSVC somehow decides to treat this ambiguity as an invalid overload instead of an error, and picks the one without ambiguity. This also appears to violate the standard.
Together, these two standard violations generate the behavior you want, mostly by accident.
I believe we can work around it in a standard compliant manner without having to write manual SFINAE at each spot. Here is an attempt:
template<class T>
struct exactly {
T t;
template<class U, std::enable_if_t<std::is_same<T, std::decay_t<U>>{}, int> =0>
exactly( U&& u ):t(std::forward<U>(u)) {}
exactly():t() {}
exactly(exactly&&)=default;
exactly(exactly const&)=default;
operator T() const& { return t; }
operator T() && { return std::move(t); }
T& get()& { return t; }
T const& get() const& { return t; }
T get()&& { return std::move(t); }
};
now use:
void functionTest(exactly<bool> b) {
std::cout << "B\n";
}
live example.
Basically we move the fancy SFINAE into a utility class, thereby avoiding polluting the function signature with SFINAE.

Operator << - order of parameter evaluation

From this question & the answers - What is the correct answer for cout << c++ << c;?
I get that
std::cout<<c++<<c;
is evaluated as:
std::operator<<(std::operator<<(std::cout, c++), c);
so the undefined behavior comes from the fact that either of the two parameters could be evaluated first. So far so good.
But why std::operator <<? Why isn't std::ostream::operator << called? And if it is, wouldn't it translate to
(ofstream::operator<<(c++)) << c;
|
returns ofstream&
What's the difference between this and method chaining:
struct A
{
A& foo();
void goo();
};
//...
A a;
a.foo().goo();
?
std::ostream provides operator<< as overloaded member operators, but other headers (e.g. <string>) provide free operators; so whether << is a member operator or a free function depends on the RHS type.
However, it doesn't matter either way. Let's rename << as foo and cout as bar:
foo(foo(bar, c++), c);
bar.foo(c++).foo(c);
In both cases behaviour is undefined because there is no requirement on the implementation to evaluate the arguments to either call to foo in any particular order. The important consideration is that per Annex C, a chained method call does not constitute more than one full-expression; if the compiler sees
foo.bar(<some-complex-expression>).baz(<another-complex-expression>);
it is free to apply CSE and reordering to the arguments to bar and to baz; indeed examination of side effects may show that the arguments to baz are evaluated before those to bar.
struct A { A &foo(int) { return *this; } };
#include <cstdio>
int main() { A().foo(printf("hello\n")).foo(printf("bye\n")); }
My compiler (gcc 4.1.2) generates a program which prints bye\nhello\n.