This question already has answers here:
How to check whether operator== exists?
(14 answers)
Closed 3 years ago.
I am writing code to compare to instances of various types. The final comparison function is quite simple - with signature:
template <typename T>
int not_equal(const T& arg1, const T& arg2) {
if (arg1 == arg2)
return 0;
std::cerr << "Error when comparing" << std::endl;
return 1;
}
Now - I would like to add the actual values being compared in the std::cerr message as:
std::cerr << "Error when comparing " << arg1 << " != " << arg2 << std::endl;
however - many of the classes do not have operator<< - and that is OK. For the classes which do not support operator<< I just want the classname - i.e. the pseudo code should be something like:
if (supports_operator<<<T>)
std::cerr << "Error when comparing " << arg1 << " != " << arg2 << std::endl;
else
std::cerr << "Error when comparing instances of type: " << typeid(arg1).name() << std::endl;
Can I have my fictitious supports_operator<<<T>()functionality?
Edit: I am limited to C++17
If you are able to use C++20 and concepts, then you can do something like this:
#include <iostream>
#include <concepts>
template <typename T>
concept Streamable = requires (T x) { std::cout << x; };
struct Foo {};
struct Bar {};
std::ostream& operator<<(std::ostream& os, Foo const& obj) {
// write obj to stream
return os;
}
template <Streamable T>
void foo(T const& t) {
std::cout << t << std::endl;
}
int main() {
Foo f;
Bar b;
foo(f);
foo(b); // error
return 0;
}
Demo
Related
I have a templated class MyClass<T> that takes some iterable containing ints (e.g. T = std::vector<int>) in its constructor and does something with it.
I would like to be able to pass the iterable as either a temporary object (e.g. MyClass(std::vector<int>{3,6,9}) or similar r-value argument) or from a named variable (resulting in an l-value as the constructor argument).
I would like to use C++17 template class inference (i.e. write MyClass(...), not MyClass<std::vector<int>>(...)).
I thought that I could declare the constructor parameter as MyClass(T && vec) (a "universal reference") to take either an l-value or an r-value (just like I can with functions), but it gives an error. It seems like T is always inferred as std::vector<int> and never std::vector<int>& with classes, whereas functions infer std::vector<int>& when the argument is an l-value.
How exactly are the rules for template constructor inference and template function inference different? Can I avoid having to use a wrapper function (e.g. myFunction(T&&vec) { return MyClass<T>(std::forward<T>(vec)); }) just for the sake of template inference?
Run the code below on Godbolt:
#include <iostream>
#include <utility>
#include <vector>
template <typename T>
using BeginType = decltype(std::declval<T>().begin());
template <typename T>
struct MyClass {
BeginType<T> begin;
BeginType<T> end;
MyClass(T && vec) {
begin = std::forward<T>(vec).begin();
end = std::forward<T>(vec).end();
}
int sum() {
int sum = 0;
for (auto it = begin; it != end; ++it) sum += *it;
return sum;
}
};
template <typename T>
MyClass<T> myFunction(T && vec) {
return MyClass<T>(std::forward<T>(vec));
}
int main() {
std::vector<int> x{1, 2, 3};
std::vector<int> y{2, 4, 6};
// Warmup: Passing r-values works fine
std::cout << MyClass(std::vector<int>{3, 6, 9}).sum() << std::endl; // works fine: T is std::vector<int>
std::cout << MyClass(std::move(y)).sum() << std::endl; // works fine: T is std::vector<int>
// Unexpected: Passing l-values doesn't work
// std::cout << MyClass(x).sum() << std::endl; // error: cannot bind rvalue reference of type 'std::vector<int>&&' to lvalue of type 'std::vector<int>'
// Compare: Passing l-values to function works fine
std::cout << myFunction(x).sum() << std::endl; // works fine: T is std::vector<int>&
}
Add a user-defined deduction guide after the class definition:
template <typename T>
struct MyClass {
// same as in question
};
template <typename TT> MyClass(TT && vec) -> MyClass<TT>;
See also How to write a constructor for a template class using universal reference arguments in C++
I am confused with some aspects of the implicit move constructor.
My understanding is that the implicitly-declared move constructor are provided by the compiler for a class iff there are no user-declared copy constructors, no copy assignment operators, no move assignment operators and no destructors.
This is the case with 'Heavy' in my example. Which behaves as expected (data is moved).
'HeavyWithDestructor' would not qualify for a implicitly-declared move constructor, because it has a destructor, but I can "std::move" it. Sort of, it is a copy (see the data pointer).
This looks to me like a trivial move constructor, in the sense that it performs the same actions as the trivial copy constructor (as if by std::memmove).
But if I don't have the conditions for the creation of a implicit move constructor in the first place, how can it be a trivial move constructor. Further more, 'std::is_trivially_move_constructible_v' indicates this is not a trivial move constructor.
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;
constexpr int largeNumber = 10000000;
#define OUT(...) std::cout << #__VA_ARGS__ << " : " << __VA_ARGS__ << '\n'
// Consistent with an implicit 'move' constructor.
class Heavy
{
vector<int> v_;
public:
Heavy() : v_(vector<int>(largeNumber)) {}
int* getDatap() { return v_.data(); }
};
// Not consistent with an implicit 'move' constructor. (Because has a destructor)
class HeavyWithDestructor
{
vector<int> v_;
public:
HeavyWithDestructor() : v_(vector<int>(largeNumber)) {}
~HeavyWithDestructor(){}
int* getDatap() { return v_.data(); }
};
int main()
{
cout << "Moving a heavy object" << endl;
OUT(std::is_move_constructible_v<Heavy>);
OUT(std::is_trivially_move_constructible_v<Heavy>);
Heavy originalHeavy;
cout << "Data* in original() -> " << originalHeavy.getDatap() << endl;
Heavy finalHeavy = move(originalHeavy);
cout << "Data* in main() -> " << finalHeavy.getDatap() << endl << endl;
cout << "Moving a heavy object with a destructor" << endl;
OUT(std::is_move_constructible_v<HeavyWithDestructor>);
OUT(std::is_trivially_move_constructible_v<HeavyWithDestructor>);
HeavyWithDestructor originalWoDestructor;
cout << "Data* in original() -> " << originalWoDestructor.getDatap() << endl;
HeavyWithDestructor finalWoDestructor = move(originalWoDestructor);
cout << "Data* in main() -> " << finalWoDestructor.getDatap() << endl;
return 0;
}
I get the following output: I can confirm I am moving 'Heavy' cause the pointers to the vector data point to the same location. I can also confirm that 'HeavyWithDestructor' is copying, not moving the data.
Moving a heavy object
std::is_move_constructible_v<Heavy> : 1
std::is_trivially_move_constructible_v<Heavy> : 0
Data* in original() -> 000001E3FB193080
Data* in main() -> 000001E3FB193080
Moving a heavy object with a destructor
std::is_move_constructible_v<HeavyWithDestructor> : 1
std::is_trivially_move_constructible_v<HeavyWithDestructor> : 0
Data* in original() -> 000001E3FD7C6080
Data* in main() -> 000001E38000A080
What is this constructor that the compiler is declaring for 'HeavyWithDestructor'?. If this constructor is not moving the data, why can I still use std::move on it?
If I try harder to make the compiler NOT declare a move constructor for me by defining a copy constructor, then I cannot use the std::move. I get compilation errors. This is what I would expect. From this, I gather the constructor I am getting is not a copy constructor. From where I initially suspected this is a trivial move constructor (that behaves as in std::memmove), but I have indications that is not right either. So what is this?
I am using vs2019 c++17 as a compiler.
HeavyWithDestructor is a typical C++03 type: copyable but not movable (what’s “movable”?). As such, for compatibility, it is copied whenever a move is attempted. The technical reason for this is that const HeavyWithDestructor& can bind to an rvalue; the moral reason is that std::move, as always, grants permission to move something but does not require it (or do so itself).
(Your experiment with a copy constructor is not detailed enough to reproduce, but might have involved HeavyWithDestructor(HeavyWithDestructor&) that is still considered a copy constructor but cannot serve as a move constructor.)
If I have a templated class, I can do the following to detect if a vector was passed:
template<typename T> struct is_vector { static const bool value=false; };
template<typename T> struct is_vector<std::vector<T>> { static const bool value=true; };
template<class T>
class Parser {
public:
Parser() {}
void parse(T obj) {
if (is_vector<T>::value) {
std::cout << "vector\n";
//obj.push_back(T {});
}
else {
std::cout << "not vector\n";
}
}
};
int main() {
Parser<int> p1;
p1.parse(123);
Parser<std::vector<int>> p2;
p2.parse({ 1, 2, 3});
return 0;
}
Output:
not vector
vector
I can detect a vector, yet the compiler complains when I uncomment the push_back call:
main.cpp: In instantiation of ‘void Parser<T>::parse(T) [with T = int]’:
main.cpp:26:14: required from here
main.cpp:15:17: error: request for member ‘push_back’ in ‘obj’, which is of non-class type ‘int’
obj.push_back(T {});
~~~~^~~~~~~~~
Obviously, an int does not have a push_back function, but the vector does. The is_vector call is evaluated at runtime, but the push_back is caught at compile time.
With partial template specialization, I can do what I want:
template<typename T>
void parse(T obj) {
std::cout << "not vector: " << obj << "\n";
}
template<typename T>
void parse(std::vector<T> obj) {
std::cout << "is vector\n";
for (auto i : obj) std::cout << i << " ";
obj.push_back(T {});
std::cout << "\n";
for (auto i : obj) std::cout << i << " ";
std::cout << "\n";
}
int main() {
parse(1);
parse('a');
parse(std::vector<int> { 1, 2, 3 });
return 0;
}
Output:
not vector: 1
not vector: a
is vector
1 2 3
1 2 3 0
So, how can I combine these 2 ideas, either at compile-time or at runtime? That is, have a templated class with a function that can handle vectors and non-vectors?
What you're looking for is a new feature in C++17, if constexpr. It's the same as a regular if, except that the condition is evaluated at compile time, and when instantiating the branch(es) will discard the non-taken branch at compile time. The discarded branch does not need to well-formed. So, for your example:
template<class T>
class Parser {
public:
Parser() {}
void parse(T obj) {
if constexpr (is_vector<T>::value) {
std::cout << "vector\n";
obj.push_back(T {});
}
else {
std::cout << "not vector\n";
}
}
};
See Difference between if constexpr vs if for some more talk on the differences. You can also read the cppreference page on if statements to get a detailed overview of some of the nitty-gritty details.
#Python code
user = input("Please enter your name \n")
print ("Your name is,", user)
How can I make this in C++?
I don't exactly know what you want to achieve, but I think this is what you're looking for.
#include<iostream>
#include<string>
using namespace std;
int main()
{
string user;
/* ---- This part is in place of your python code --- */
cout << "Please Enter your name"; cin >> user;
cout << "Your name is" << user;
/* --------------------- */
return 0;
}
Unfortunately, the accepted answer does not match the legitimate question, how to realize the input.
Consider this solution:
#include <iostream>
#include <string>
template<typename T>
T input(std::string message = "")
{
if (!empty(message)) std::cout << message << " : ";
T value;
std::cin >> value;
return value;
}
int main()
{
auto i = input<int>("integer, followed by decimal point value");
auto d = input<double>();
if (std::cin) std::cout << "Gelesen: " << i << ' ' << d << '\n';
else std::cout << "error reading input";
}
The input() function does not return a string like in Python, but a value of the type indicated in angle brackets.
I have written a templates stack linked list for an assignment I have to complete. When I run it, it prints out the first element, but then crashes. Any ideas on what could be going wrong? I have a feeling it might be the pop function, but its so basic, I'm not sure what is even wrong.
template<typename T>
struct StackNode
{
T data;
StackNode* next;
StackNode(T t, StackNode* ptr);
~StackNode();
};
template <typename T>
StackNode<T>::StackNode(T t, StackNode* ptr = nullptr)
:data(t), next(ptr)
{
}
template <typename T>
StackNode<T>::~StackNode()
{
delete next;
}
template<typename T>
struct LinkedStack
{
LinkedStack();
LinkedStack(const StackNode<T> &s);
~LinkedStack();
bool isEmpty();
void push(const T& t);
void pop();
T top();
StackNode<T>* head;
int numElements;
};
template <typename T>
LinkedStack<T>::LinkedStack()
: head(nullptr), numElements(0)
{
}
template<typename T>
LinkedStack<T>::LinkedStack(const StackNode<T> &s)
: head(nullptr), numElements(0)
{
for (auto t = s.head; t; t = t->next)
push(t->item);
}
template<class T>
LinkedStack<T>::~LinkedStack()
{
while (!isEmpty())
{
pop();
}
}
template<typename T>
bool LinkedStack<T>::isEmpty()
{
if (numElements > 0)
{
return false;
}
else
{
return true;
}
}
template<typename T>
void LinkedStack<T>::push(const T& t)
{
head = new StackNode<T>(t, head);
numElements++;
}
template<typename T>
void LinkedStack<T>::pop()
{
if (isEmpty())
{
cout << "Stack is empty." << endl;
}
else
{
StackNode<T>* oldnode = head;
head = head->next;
numElements--;
delete oldnode;
}
}
template<typename T>
T LinkedStack<T>::top()
{
if (isEmpty())
{
cout << "Stack is empty..." << endl;
}
else
{
return head->data;
}
}
int main()
{
LinkedStack<string> x;
x.push("Test");
x.push("One");
x.push("Two");
x.push("Three");
cout << "Now popping all elements of the stack:" << endl;
while (x.isEmpty() == false)
{
cout << x.top() << endl;
x.pop();
}
}
When you delete a node, you also delete the node it points to :
template <typename T>
StackNode<T>::~StackNode()
{
delete next;
}
When you pop an item, you delete the whole linked list as the deletion propagate from a node to another. The segfault is raised after deleting the last node, your programs try to delete nullptr.
The delete statement in the pop function is enough. No need to delete Stacknode.next as you do not assign it with the new keyword
I've successfully runned your code after removing the problematic delete.