Here's my attempt at creating a simple constexpr linked list -
struct Node
{
constexpr Node(const int n, Node const* next = nullptr)
: value(n), next(next) {}
constexpr Node push(const int n) const { return Node(n, this); }
int value;
Node const* next;
};
constexpr auto getSum(Node n) {
int sum = 0;
Node *current = &n;
while(current != nullptr) {
sum += current->value;
current = current->next;
}
return sum;
}
int main() {
constexpr Node a(0);
a.push(1);
a.push(22);
constexpr auto result = getSum(a);
return result;
}
on compiling this program, the following error is shown
prog.cc: In function 'constexpr auto getSum(Node)':
prog.cc:16:28: error: invalid conversion from 'const Node*' to 'Node*' [-fpermissive]
current = current->next;
~~~~~~~~~^~~~
prog.cc: In function 'int main()':
prog.cc:25:35: in constexpr expansion of 'getSum(a)'
prog.cc:16:28: error: conversion of 'const Node*' null pointer to 'Node*' is not a constant expression
How should I proceed forward to solve this issue and generate such linked list? Here is Wandbox Link for seeing the execution online.
The immediate error is straightforward to fix:
Node const *current = &n;
// ^^^^^
The complaint is that current = current->next; is assigning a Node const* to a Node *, so don't do that.
Doing that will make your program compile but print 0, because neither push calls modified a. You also can't store the result of push as constexpr since the address of a, an automatic local variable, isn't a constant expression.
You can, however, form a linked list of temporary nodes and immediately use it:
constexpr auto result = getSum(a.push(1).push(22).push(19)); // OK, 42
As #hg_git pointed out in the commentary of your post, a constexpr linked list is not possible.
I cleaned up your code to have a useful error.
#include <iostream>
struct Node
{
constexpr Node(const int n, Node * next = nullptr)
: value(n), next(next) {}
constexpr Node push(const int n) { return Node(n, this); }
int value;
Node * next;
};
constexpr auto getSum(Node n) {
int sum = 0;
Node *current = &n;
while(current != nullptr) {
sum += current->value;
current = current->next;
}
return sum;
}
int main() {
constexpr Node a(0);
a.push(1);
a.push(22);
constexpr auto result = getSum(a);
return result;
}
Giving this
main.cpp: In function 'int main()':
main.cpp:25:13: error: passing 'const Node' as 'this' argument discards qualifiers [-fpermissive]
a.push(1);
^
main.cpp:7:20: note: in call to 'constexpr Node Node::push(int)'
constexpr Node push(const int n) { return Node(n, this); }
^~~~
main.cpp:26:14: error: passing 'const Node' as 'this' argument discards qualifiers [-fpermissive]
a.push(22);
^
main.cpp:7:20: note: in call to 'constexpr Node Node::push(int)'
constexpr Node push(const int n) { return Node(n, this); }
As you can see, even if the keyword const is not there, there is still some problems with const parameters. This comes from the fact that constexpr are calculated at compilation time. Thus making them immutable at run time.
A linked list could change at runtime; if you add or remove a Node per example.
So constexpr are not the right choice in this case.
Edit :
Here's a live demo of your code, clean from constexpr. I added some commentary, feel free to ask if you don't understand a line or a function.
Related
I am practicing C++ code snippets in the CPP Crash Course Book (Chapter-8, Exercise 1), Link the below does not compile and the error of the program has been provided below. It would be helpful if any suggestions or links for understanding is provided. :).
#include <cstdio>
struct FibonacciIterator {
bool operator!=(int x) const {
return x >= current;
}
FibonacciIterator& operator++() {
const auto tmp = current;
current += last;
last = tmp;
return *this;
}
int operator*() const {
return current;
}
private:
int current{ 1 }, last{ 1 };
};
struct FibonacciRange {
explicit FibonacciRange(int max) : max{ max } { }
FibonacciIterator begin() const {
return FibonacciIterator{};
}
int end() const {
return max;
}
private:
const int max;
};
int main() {
for (const auto i : FibonacciRange{ 50 }) {
printf("%d ", i);
}
}
Error:
user#stretch:/tmp$ g++ test.cpp -o test
test.cpp: In function ‘int main()’:
test.cpp:32:46: error: inconsistent begin/end types in range-based ‘for’ statement: ‘FibonacciIterator’ and ‘int’
for (const auto i : FibonacciRange{ 5000 }) {
^
test.cpp:32:46: error: conversion from ‘int’ to non-scalar type ‘FibonacciIterator’ requested
test.cpp:32:46: error: no match for ‘operator!=’ (operand types are ‘FibonacciIterator’ and ‘FibonacciIterator’)
test.cpp:3:10: note: candidate: bool FibonacciIterator::operator!=(int) const
bool operator!=(int x) const {
^~~~~~~~
test.cpp:3:10: note: no known conversion for argument 1 from ‘FibonacciIterator’ to ‘int’
Your begin and end member functions return different types. Pre-C++17, these types need to be the same, as the error message says
error: inconsistent begin/end types in range-based ‘for’ statement
From C++17, this restriction on range-for loops was relaxed
As of C++17, the types of the begin_expr and the end_expr do not have to be the same, and in fact the type of the end_expr does not have to be an iterator: it just needs to be able to be compared for inequality with one. This makes it possible to delimit a range by a predicate (e.g. "the iterator points at a null character").
So if you compile your code with the C++17, by passing the -std=c++17 flag, your code will compile, assuming it satisfies all other constraints, which it does.
How does this compile and run without any warnings or errors? I don't understand how the dereferenced value of current, which is an int, can be assigned to the string a without any problem.
class Test {
public:
string a;
Test(initializer_list<int> t) {
auto current = t.begin();
// I am assigning an int to a string!
a = *current;
}
};
int main() {
Test test{65};
printf("%s\n", test.a.c_str());
}
The printed string is
A
In contrast, this very similar code produces a compile-time error:
int main() {
initializer_list<int> test1{65};
auto current = test1.begin();
string b = *current;
return 0;
}
The error is:
error: no viable conversion from 'const int' to 'std::__1::string' (aka 'basic_string<char, char_traits<char>, allocator<char> >')
string b = *current;
Note that a = *current; and string b = *current; perform different things.
a = *current; is an assignment, which leads to the invocation of operator=, and std::string::operator= has an overloading taking char, which makes a = *current; work (after the implicit conversion from int to char).
4) Replaces the contents with character ch as if by assign(std::addressof(ch), 1)
string b = *current; is an initialization, which tries to call the constructor of std::string to initialize b. But these constructors don't have such overloading taking int (or char), then string b = *current; won't work.
I am trying to implement linkedlist, and i have some problem with operator[]
template <class T>
T& L1List<T>::at(int i){
L1Item<T> * pRet = this->_pHead;
int idx = 0;
while(pRet){
if(i != idx){
pRet = pRet->pNext;
idx++;
}else return (pRet->data);
}
}
template <class T>
T& L1List<T>::operator[](int i){
return at(i);
}
and when i compile it, it runs with list->at(i), but list[i].
int a = list[i]; the error is that "cannot convert L1List'<'int> to ‘int’ in initialization"
If list->at(i) works, it means that list is a pointer, not an object. Hence, list[i] evaluates to an object. That's why int a = list[i]; does not work, which also explains the error message. You cannot use a L1List<int> to initialize an object of type int.
You need to use:
int a = (*list)[i];
or make it really complicated and use:
int a = list->operator[](i);
I tried to create my own structure. So I wrote this piece of code.
struct node
{
int val, id;
node(int init_val, int init_id)
{
val = init_val;
id = init_id;
}
};
node t[100];
int main()
{
...
}
I tried to compile my program. But I got an error:
error: no matching function for call to 'node::node()'
note: candidates are:
note: node::node(int, int)
note: candidate expects 2 arguments, 0 provided
note: node::node(const node&)
note: candidate expects 1 argument, 0 provided
node t[100];
will try to initialise the array by calling a default constructor for node. You could either provide a default constructor
node()
{
val = 0;
id = 0;
}
or, rather verbosely, initialise all 100 elements explicitly
node t[100] = {{0,0}, {2,5}, ...}; // repeat for 100 elements
or, since you're using C++, use std::vector instead, appending to it (using push_back) at runtime
std::vector<node> t;
This will fix your error.
struct node
{
int val, id;
node(){};
node(int init_val, int init_id)
{
val = init_val;
id = init_id;
}
};
You should declare default constructor.
I have this class for double linked lists:
template <typename T>
class Akeraios
{
struct node
{
T data;
node* prev;
node* next;
node(T t, node* p, node* n) : data(t), prev(p), next(n) {}
};
node* head;
node* tail;
public:
Akeraios() : head( NULL ), tail ( NULL ) {}
template<int N>
Akeraios( T (&arr) [N]) : head( NULL ), tail ( NULL ) //meta apo : simainei einai initializer list--arxikopoiisi listas
{
for( int i(0); i != N; ++i)
push_back(arr[i]);
}
bool empty() const { return ( !head || !tail ); }
operator bool() const { return !empty(); }
void push_back(T);
void push_front(T);
T pop_back();
T pop_front();
~Akeraios()
{
while(head)
{
node* temp(head);
head=head->next;
delete temp;
}
}
};
and somewhere in main
int arr[num1len];
int i=1;
Akeraios <int> dlist ( arr );//error line!!
for(i=1;i<=num1len;i++){
double digit;
int div=10;
int j;
for(j=1;j<=i;j++)div=div*div;
digit=number1/div;
int dig=(int) digit;
the error in error line is:
no matching function for call to `Akeraios::Akeraios(int[((unsigned int)((int)num1len))])'
candidates are: Akeraios::Akeraios(const Akeraios&)
note Akeraios::Akeraios() [with T = int]
try this:
Akeraios <int>* dlist = new Akeraios( arr );
your compiler thinks you're calling a function doing it the way you do it.
you could also use the implicit constructor
Akeraios<int> dlist = arr;
(not very nice this is)
This code is perfectly valid and compliant as-is. The only way I can see it messing up is if you had a compiler extension for VLAs and attempted to call the constructor with a variable-length array, which would almost certainly fail. Otherwise, it is perfectly legitimate. Visual Studio accepts without quarrel.
Since you're saying that num1len is a variable:
Templates are evaluated at compile-time. When you say arr[num1len], you're specifying an array with a variable length (is this C99 or something?). The template expects an array with a size that can be evaluated at compile time (you're saying template<int N>Akeraios( T (&arr) [N]), so there's no way the compiler can match that up.
Just imagine you had a specialization for N=5 or N=10. How would the compiler be able to find the right specialization when compiling the code if the size of the array is not known at that point?