I have a nice problem with a friend operator declared in a template class and an explicit instantiation of the class like this:
template<class T>
class IneqCmp{
friend bool operator!=(T a, T b){
return !(a==b);
}
};
struct IntCont{
int x;
bool operator==(IntCont that) const{
return x==that.x;
}
};
template class IneqCmp<IntCont>;
int main(){
IntCont a{1}, b{2};
while(a!=b);
}
I think operator!=(IntCont, IntCont) should be instantiated at the point of instantiation of IneqCmp<IntCont> but it isn't. Why?
The problem with the code above is not whether operator!= is or not defined, but the fact that it will never be found by lookup*.
When you declare a function as a friend of a class the declaration is strange in the sense that it declares a namespace level function, but the declaration of it will only be available through ADL on the enclosing type. Because your operator!= does not take IneqComp as any of the two arguments, it is effectively impossible to find through lookup.
If what you want to do is providing a default implementation of the operator!= that will be available to a set of types, you can tackle the problem in different ways.
First, you can add IneqCmp to the ADL set by using inheritance:
struct IntCont : IneqCmp<IntCont> {
// ...
};
Because ADL looks in the class of the argument, and also in the base classes, this will effectively trigger lookup inside IneqCmp<IntCont> and that will find the friend declaration and thus the free function.
Another alternative would be to add a namespace where the generic operator!= will be defined (so that it won't be found otherwise) and a tag type. Then inherit from that tag:
namespace inequality_comparable {
struct tag {};
template <typename T>
bool operator!=( T const & a, T const & b ) {
return !(a==b);
}
}
struct IntCont : inequality_comparable::tag {
};
bool operator==( IntCont const & a, IntCont const & b ) {
return ...;
}
int main() {
IntCont a,b;
std::cout << a != b << "\n";
}
The trick here is that because inequality_comparable::tag is a base of your type, it will add the namespace inequality_comparable to lookup. When the compiler encounters a != b in main it will try to use an operator!= defined in the current scope, the class IntCont and it's bases and the namespace where IntCont and its bases are defined.
* If you want to verify that the operator has actually be generated, try to add it:
bool operator!=(IntCont a, IntCont b){
return !(a==b);
}
If the operator has been defined because of the instantiation of IneqCmp then that will trigger a compiler error. Alternatively, you can just compile and inspect the generated object file for the symbol.
Related
I understand that for a class
class A {
const int myint;
public:
A (const int yourint);
A (const std::string yourstring);
};
I could initialize myint in the initializer list like so:
A::A (const int yourint) : myint (yourint) {};
However, what is the proper way of initializing myint from the second constructor if the the data required to compute it comes say from a string and the computations may be involved?
Use a function call inside a delegating (if avaliable, not neccessarily) constructor's member initialization list:
A::A(std::string const& yourstring) : A(compute_myint(yourstring)) {};
Pass std::string by const&, not just const, while you're at it.
compute_myint can be non-member, static member, possibly not accessible from outside the class, whichever makes the most sense.
Here you would want to use delegating constructors, if you can, or you could compute in the ctor. See my second example for the second option. An example for your class would be:
Option 1: Delegating Constructors: C++11 forward
class A {
const int myint;
static int parse_int(const std::string& string) {/*...*/}
public:
A (const int yourint) : myint{yourint};
A (const std::string yourstring) : A{parse_int(yourstring)};
}
By the way, since parse_int only computes integers, then it could be static, meaning it does not require a class instance to be used. Of course, there is no requirement, as the function could be a member just as well, (non static), although static is safer, since it will almost always guarantee the construction of the object.
Option 2: Constructor Computation, non delegating
This method could be used in any C++ version.
class A {
const int myint;
static int parse_int(const std::string& string) {/*...*/}
public:
A (const int yourint) : myint(yourint);
A (const std::string yourstring) : my_int(parse_int(yourstring));
}
Just use a member function.
Keep in mind that it's safer (i.e. less error-prone) to use a static member function for things like this than a non-static one, because the class isn't fully initialized yet when the function is called.
class A {
const int myint;
public:
A(const int x) : myint(x) {}
A(std::string const& s) : myint(compute(s)) {}
private:
static int compute(std::string const& s) { return (int)s.length(); }
};
I've been annoyed by this issue quite a few times, so I have developed a small utility to solve it in the general case. The full code is as follows:
namespace initBlock_detail {
struct tag { };
template <class F>
decltype(auto) operator + (tag, F &&f) {
return std::forward<F>(f)();
}
}
#define initBlock \
initBlock_detail::tag{} + [&]() -> decltype(auto)
And it is used as follows:
int const i = initBlock {
// Any complex calculation
// and then return the value
return foo;
};
See it live on Coliru
The structure is similar to Andrei Alexandrescu's ScopeGuard implementation, which uses an infix operator overload and a lambda to achieve that light syntax. i's type can be deduced, can be a reference, etc. Other useful features include the possibility to place using namespace declarations inside the init-block. Any movable and/or copyable type can be used.
I wrote my own vector and stack, they are class template,both works fine,now I want to Specializes the std::swap algorithm for my stack,so I defined a function template swap as friend of vector, it uses the swap inside vector.then I defined a swap member function in stack.but if I try to call that swap function, it can't compile. the vs2019 says the "the swap function doesn't accept two arguments". I thought the swap inside stack class would use argument dependent lookup,but it didn't do that.please help me,what is the right way to do it? thanks a lot. some code of vector and stack are below
Vector.h
template<class Object>
void swap(Vector<Object>& lhs, Vector<Object>& rhs) {
lhs.swap(rhs);
}
template<class Object>
class Vector {
//.....
friend void swap<>(Vector<Object>& lhs, Vector<Object>& rhs);
public:
//...
void swap(Vector &rhs){
//....
}
}
Stack.h
#include"Vector.h"
template<class Object,class Container>
class Stack {
//......
void swap(Stack &other) {
swap(container, other.container);
}
main.cpp
//....
int main() {
//....
Stack<int,Vector<int>>s1,s2;
s1.swap(s2);
}
//....
It's a simple case of name hiding: In the Stack class the name swap is Stack::swap.
If you want to use some other function you need to provide an explicit scope, as in
::swap(container, other.container);
Note the use of the :: scoping operator to use the swap function from the global scope.
Here:
void swap(Stack &other) {
swap(container, other.container);
}
You are calling swap from itself. You don't want that. To make it so ADL finds the right swap function, you can try this:
// outside the class
template <typename T>
void MySwap(T& a1, T& a2) {
using std::swap; // may not be needed in your case
swap(a1, a2);
}
// in the class
void swap(Stack &other) {
MySwap(container, other.container);
}
The name swap inside MySwap cannot possibly refer to Stack::swap, so it will use ADL to find the correct overload.
For more on why using std::swap is useful but possibly unnecessary in your case, see: https://en.cppreference.com/w/cpp/named_req/Swappable
Working on a personal project between terms and totally stuck. Any help is appreciated. None of this is covered in any of my books so I searched in help documentation, discovered predicates, then searched online for how to use the third parameter to sort. The third argument won't compile. I've also tried doing this is a free function, which also doesn't compile, but this is as close as I can get.
Compiletime error "identifier not found" is noted in comment below. Also I've omitted a ton of code; if I comment out the one line then I have no errors and every other method call and functionality works fine, including using the list.sort() call which utilizes the overridden global operator<() I believe.
class StudyCategory
{
private:
std::string label;
std::list<StudyItem> studyItems;
public:
// Constructors etc. omitted
void sortByStartDate();
void sortByEndDate();
};
/*
***** Compile time error "GreaterEndDate identifier not found" ****
*/
void StudyCategory::sortByEndDate()
{
std::sort(studyItems.begin(), studyItems.end(), GreaterEndDate());
}
I included in the same file the following predicate function, which by all my documentation, seems to be correct and does compile fine:
class GreaterEndDate
{
public:
bool operator() (const StudyItem& a, const StudyItem& b) const
{
return a.getEndDate() < b.getEndDate();
}
};
And the StudyItem class has overloaded comparison operators set up as friends of the class. This may not be perfect but it's how the textbook I'm using works.
class StudyItem
{
private:
int startDate, endDate;
public:
int getStartDate() const;
int getEndDate() const;
void setStartDate(int date);
void setEndDate(int date);
// Overloaded operators
friend bool operator==(StudyItem a, StudyItem b);
friend bool operator<(StudyItem a, StudyItem b);
friend bool operator>(StudyItem a, StudyItem b);
};
// Friend functions - operator overrides
bool operator==(StudyItem a, StudyItem b)
{
return a.getStartDate() == a.getStartDate();
}
bool operator<(StudyItem a, StudyItem b)
{
return a.getStartDate() < a.getStartDate();
}
bool operator>(StudyItem a, StudyItem b)
{
return a.getStartDate() > a.getStartDate();
}
I'm sorry if such a question is asked before but i could not find no matter how much I searched (there were similar questions but none of them seemed to work for me).
I am writing a templated class and everything works fine except when I try to overload operator+. I try to overload operator+ two times for different parameters but compiler does not see one of the definitions and gives an eror. Here's the code:
Worker.h (one of my previos homeworks, implemented the problem here since it is easier to see ):
#ifndef _WORKER_H
#define _WORKER_H
#include <string>
#include "Project.h"
using namespace std;
template <class T>
class Worker {
public:
Worker(const string &, Project &);
void Work(const int &);
const Worker & Worker::operator+(const int &); //Problem
const Worker & Worker::operator+(const string &); //Problem
string getName() const;
int getTime() const;
private:
string myName; //The name of the worker
Project & myProject;
int myTime; //Variable to keep track of how much the worker worked.
};
#include "Worker.cpp"
#endif
and the relevant part of Worker.cpp:
template <class T>
const Worker<T> & Worker<T>::operator+(const int & add)
{
cout << add;
return *this;
}
template <class T>
const Worker<T> & Worker<T>::operator+(const string & add)
{
cout << add;
return *this;
}
+ operators are not doing anything right now, the problem is the compiler only sees first declared function (in this case the with the parameter int). There also does not seem to be problem with the functions because if I try to overload only one time, both of them work fine alone. Also I can use them (making the necessary changes) in a non-templated class.
I think it is something simple but since I'm new to templates I could not figure out what the problem was.
There are a few problems with your approach, unrelated to templates.
First your operators would only work for one ordering of operations: Worker<T> + int, and not int + Worker<T>
Second typically you would want to return a new Worker instance, not return this by reference, because A+Bshould not modify A or B.
So what you could do is define non-member operators for the different orderings:
template <typename T>
Worker<T> operator+(int i, const Worker<T>& t) { }
template <typename T>
Worker<T> operator+(const Worker<T>& t, int i) { }
and so on.
Note that for operators that should affect the state of the object, such as +=, *= and so on, the usual approach is to use member operators and return by reference, because in these cases it makes perfect sense.
2 things here, 1 of which is your issue
The return type of a member function is not affected by the fact that the class is a template, hence the declaration of operator+ returning Worker and the definition returning Worker<T> are different. In the class definition, they should be:
const Worker<T> & operator+(const int &); //Problem
const Worker<T> & operator+(const string &); //Problem
The other thing which is also changed in the above code is you don't need the scope (Worker::) in a class declaration
Working on a simple example for template functions. The code compiles and works as expected. But my question is why "static" is required in both "Cmp" and "Lit"? Otherwise, it will not compile?
Thanks a lot!
template<class T> class Cmp{
public:
static int work(T a, T b) {
std::cout << "Cmp\n";
return 0;
}
};
template<class T> class Lit{
public:
static int work(T a, T b){
std::cout << "Lit\n" ;
return 0;
}
};
template<class T, class C>
int compare(const T &a, const T &b){
return C::work(a, b);
}
void test9(){
compare<double, Cmp<double> >( 10.1, 20.2);
compare<char, Lit<char> >('a','b');
}
C::work(a, b) names a static member function work() of class C.
The reason that static is required here is that in the compare template function, you have this line:
return C::work(a, b);
The syntax C::work(a, b) here means "call the function work nested inside the class C. Normally, this would try to call a member function without providing a receiver object. That is, typically the way you'd call a function work would be by writing
C myCObject;
myCObject.work(a, b);
In this case, though, we don't want to be calling a member function. Instead, we want the function work to be similar to a regular function in that we can call it at any time without having it act relative to some other object. Consequently, we mark those functions static so that they can be called like regular functions.
Hope this helps!