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.
Related
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.
In the code below I am wondering how exactly std::tuple_size<T>{} and std::tuple_size<T>() return the size of the tuple. When looking at the docs for using this call, it seems that I should either have to use the () operator or the static ::value member variable in order to get the tuple size. However, when running this code it compiles and produces the correct output, how is tuple_size returning the value from the constructor?
#include <iostream>
#include <tuple>
template <class T>
void test(T)
{
//std::make_index_sequence<std::tuple_size<T>{}>{} Seen used like this spurned my ?
std::cout << std::tuple_size<T>{} << '\n';
std::cout << std::tuple_size<T>() << '\n';
std::cout << std::tuple_size<T>()() << '\n';
std::cout << std::tuple_size<T>::value << '\n';
}
int main()
{
test(std::make_tuple(1, 2, 3.14));
}
http://en.cppreference.com/w/cpp/utility/tuple/tuple_size
Says:
Member constants
value
[static]
sizeof...(Types) (public static member constant)
Member functions
operator std::size_t
converts the object to std::size_t, returns value (public member
function)
operator()
(C++14)
returns value (public member function)
So no wonder.
1st & 2nd form use conversion operator.
3rd form explicitly calls function call operator.
4th form uses static member.
This is an exceptionally convenient std utility. No other std tool has as many convenience functions.
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.
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.
I'm having trouble with some valarray function pointer code:
double (*fp)(double) = sin;
valarray<double> (*fp)(const valarray<double> &) = sin;
The first compiles, the second gives:
error: no matches converting function 'sin' to type 'class std::valarray<double> (*)(const class std::valarray<double>&)'
This compiles, using the __typeof__ GCC extension. Looks like GCC's valarray uses expression templates to delay calculation of the sinus. But that will make the return type of the sin template not exactly valarray<T>, but rather some weird complex type.
#include <valarray>
template<typename T> struct id { typedef T type; };
int main() {
using std::valarray;
using std::sin;
id<__typeof__(sin(valarray<double>()))>::type (*fp)(const valarray<double> &) = sin;
}
Edit: See AProgrammer's standard quote for why GCC is fine doing that.
Edit: Standard compliant workaround
Doing this without __typeof__ in a strictly Standard conforming way is a bit tricky. You will need to get the return type of sin. You can use the conditional operator for this, as Eric Niebler has shown. It works by having the sin function not actually called, but only type-checked. By trying to convert the other branch (the one which is actually evaluated) of the conditional operator to that same type, we can generate a dummy parameter just to be able to deduce the type of the function pointer:
#include <valarray>
using std::valarray;
template<typename T> struct id {
typedef T type;
};
struct ded_ty {
template<typename T>
operator id<T>() { return id<T>(); }
};
template<typename E, typename T>
id<T(*)(valarray<E> const&)> genFTy(T t) {
return id<T(*)(valarray<E> const&)>();
}
template<typename T>
void work(T fp, id<T>) {
// T is the function pointer type, fp points
// to the math function.
}
int main() {
work(std::sin, 1 ? ded_ty() : genFTy<double>(std::sin(valarray<double>())));
}
If you want to get the address right away, you can write work so it returns fp again.
template<typename T>
T addy(T fp, id<T>) { return fp; }
Now, you can finally write a macro to encapsulate the conditional operator trickery, and use it when you want to get the address of any such math function.
#define DEDUCE(FN,Y) (1 ? ded_ty() : genFTy<Y>(FN(std::valarray<Y>())))
To get the address and pass it to some generic function, the following works then
std::transform(v1.begin(), v1.end(), v1.begin(),
addy(std::sin, DEDUCE(std::sin, double)));
std::transform(v2.begin(), v2.end(), v2.begin(),
addy(std::cos, DEDUCE(std::cos, double)));
26 3.1/3
Any function returning a valarray is permitted to return an object of another type,
provided all the const member functions of valarray are also applicable to this type.
The aim is to allow template expressions to be used to optimize the result (i.e. looping one time on the whole array doing each times the computation, directly assigning to the resulting valarray<> instead of building a temporary).
z = sin(x+y);
can be optimized to
for (i = 0; i < N; ++i)
z[i] = sin(x[i] + y[i]);
You speak about std::sin in the title, but then assign ::sin.
valarray<double> (*fp)(const valarray<double> &) = std::sin;
That should work. Note, that you should qualify all uses of sin, though most implementations will inject the name to the global namespace even if you include <cmath> (that is non-standard behavior).
Edit: unfortunately, you're out of luck. The standard says about sin(valarray<T> const &) the following (26.3.3.3).
This function shall return a value which is of type T or which can be unambiguously
converted to type T.
Optimizations performed by gcc are granted by the standard. The code above is not guaranteed to work.