"use of deleted function" error when I'm passing an object through a constructor.

Hey guys!

I'm working on a little program that turns TODO comments into GitHub issues. Everything was going fine until I started working on my parser. Whenever I pass the lexer object into the parser constructor, I get a "use of deleted function" error and I'm not too sure on what's going on.

Here's the full error:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
snitch.cpp: In function ‘int main(int, char**)’:
snitch.cpp:21:37: error: use of deleted function ‘Lexer::Lexer(const Lexer&)’
   21 |         Parser parser = Parser(lexer);
      |                                     ^
In file included from snitch.cpp:3:
lexer.h:9:7: note: ‘Lexer::Lexer(const Lexer&)’ is implicitly deleted because the default definition would be ill-formed:
    9 | class Lexer
      |       ^~~~~
lexer.h:9:7: error: use of deleted function ‘std::__cxx11::basic_stringstream<_CharT, _Traits, _Alloc>::basic_stringstream(const std::__cxx11::basic_stringstream<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
In file included from /usr/include/c++/11/bits/quoted_string.h:38,
                 from /usr/include/c++/11/iomanip:45,
                 from /usr/include/c++/11/bits/fs_path.h:39,
                 from /usr/include/c++/11/filesystem:45,
                 from file.h:7,
                 from snitch.cpp:2:
/usr/include/c++/11/sstream:1057:7: note: declared here
 1057 |       basic_stringstream(const basic_stringstream&) = delete;
      |       ^~~~~~~~~~~~~~~~~~
In file included from snitch.cpp:4:
parser.h:15:22: note:   initializing argument 1 of ‘Parser::Parser(Lexer)’
   15 |         Parser(Lexer lexer);
      |                ~~~~~~^~~~~
In file included from token.h:4,
                 from parser.h:4,
                 from parser.cpp:1:
lexer.h:40:9: error: ‘Token’ does not name a type
   40 |         Token get_token();
      |         ^~~~~
parser.cpp: In constructor ‘Parser::Parser(Lexer)’:
parser.cpp:5:23: error: use of deleted function ‘Lexer& Lexer::operator=(const Lexer&)’
    5 |         this->lexer = lexer;
      |                       ^~~~~
In file included from token.h:4,
                 from parser.h:4,
                 from parser.cpp:1:
lexer.h:9:7: note: ‘Lexer& Lexer::operator=(const Lexer&)’ is implicitly deleted because the default definition would be ill-formed:
    9 | class Lexer
      |       ^~~~~
lexer.h:9:7: error: use of deleted function ‘std::__cxx11::basic_stringstream<_CharT, _Traits, _Alloc>& std::__cxx11::basic_stringstream<_CharT, _Traits, _Alloc>::operator=(const std::__cxx11::basic_stringstream<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
In file included from lexer.h:6,
                 from token.h:4,
                 from parser.h:4,
                 from parser.cpp:1:
/usr/include/c++/11/sstream:1101:7: note: declared here
 1101 |       operator=(const basic_stringstream&) = delete;
      |       ^~~~~~~~
In file included from /usr/include/stdio.h:33,
                 from /usr/include/c++/11/cstdio:42,
                 from /usr/include/c++/11/ext/string_conversions.h:43,
                 from /usr/include/c++/11/bits/basic_string.h:6608,
                 from /usr/include/c++/11/string:55,
                 from /usr/include/c++/11/bits/locale_classes.h:40,
                 from /usr/include/c++/11/bits/ios_base.h:41,
                 from /usr/include/c++/11/ios:42,
                 from /usr/include/c++/11/ostream:38,
                 from /usr/include/c++/11/iostream:39,
                 from parser.h:3,
                 from parser.cpp:1:


main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "snitch.h"
#include "file.h"
#include "lexer.h"
#include "parser.h"
#include "token.h"

void initialize(char* argv)
{
	std::string config_file = file::get_git_config_file(argv);

	git_url = file::get_github_repo_url(config_file);
	git_token = file::get_file_contents("token.txt");
}

int main(int argc, char* argv[])
{	
	initialize(argv[1]);
	std::string phrase = "//";

	Lexer lexer = Lexer(phrase);
	Parser parser = Parser(lexer);

	parser.program();

	std::cout << "Parsing completed." << std::endl;

	return 0;
}


lexer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include "lexer.h"

Lexer::Lexer(std::string source) 
{
	this->source = source + "\n";
	this->curr_pos = -1;
	this->source_size = source.size()+1;
	this->curr_char = NULL;
	this->is_comment = false;
	advance();
}

void Lexer::advance()
{
	this->curr_pos++;

	if(this->curr_pos >= this->source_size)
		this->curr_char = '\0';

	else
		this->curr_char = this->source[this->curr_pos];
}

char Lexer::peek() 
{
	if(this->curr_pos+1 >= this->source_size)
		return '\0';

	return this->source[this->curr_pos+1];
}

void Lexer::skip_whitespace()
{
	while(this->curr_char == ' ' || this->curr_char == '\t')
		advance();
}

void Lexer::clear_stream()
{
	this->temp.str(std::string());
}

std::string Lexer::type_to_keyword(TokenEnum type)
{
	switch(type)
	{
	case 200:
		return "TODO";
		break;
	}
}

Lexer::TokenEnum Lexer::keyword_to_type(std::string keyword)
{
	if(keyword == "TODO")
		return TOKEN_TODO;

	else
		return TOKEN_TODO_TEXT;
}

Token Lexer::get_token()
{
	skip_whitespace();
	clear_stream();
	Token token;

	if(this->curr_char == '\n')
	{
		token = Token(TOKEN_NEWLINE, std::string{this->curr_char});
		this->is_comment = false;
	}	

	else if(this->curr_char == '\0')
	{
		token = Token(TOKEN_EOF, std::string{this->curr_char});
	}

	else if(this->curr_char == '/')
	{
		
		this->temp.put(this->curr_char);
		if(peek() == '/')
		{
			advance();
			this->temp.put(this->curr_char);
			token = Token(TOKEN_COMMENT, this->temp.str());
			this->is_comment = true;
		}
	}

	else if(this->curr_char == ':' && this->is_comment)
	{
		token = Token(TOKEN_COLON, std::string{this->curr_char});
	}

	else if(isalpha(this->curr_char) && this->is_comment) 
	{
		this->temp.put(this->curr_char);
		
		while(isalpha(peek()))
		{
			advance();
			this->temp.put(this->curr_char);
		}

		TokenEnum token_type = keyword_to_type(this->temp.str());
		token = Token(token_type, this->temp.str());
	}

	else if(!this->is_comment)
	{
		token = Token(TOKEN_IGNORE, "NULL");
	}

	advance();
	return token;
}


lexer.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include "token.h"

class Lexer 
{
private:
	std::string source;
	int source_size;
	int curr_pos;
	char curr_char;
	std::stringstream temp;
	bool is_comment;

public:
	enum TokenEnum {
		TOKEN_EOF = -1,
		TOKEN_NEWLINE = 0,
		TOKEN_TODO_TEXT = 1,
		TOKEN_IGNORE = 99,

		TOKEN_COMMENT = 100,
		TOKEN_COLON = 101,

		TOKEN_TODO = 200
	};

	Lexer() = default;
	Lexer(std::string source);
	void advance();
	char peek();
	void skip_whitespace();
	void clear_stream();
	std::string type_to_keyword(TokenEnum type);
	TokenEnum keyword_to_type(std::string keyword);
	Token get_token();

};


parser.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include "parser.h"

Parser::Parser(Lexer lexer)
{
	this->lexer = lexer;
	this->current_token = NULL;
	this->peek_token = NULL;
	advance();
	avance();
}

bool Parser::check(int token_type)
{
	return token_numb == this->current_token.type;
}

bool Parser::check_peek(int token_type)
{
	return token_numb == this->peek_token.type;
}

void Parser::advance()
{
	this->current_token = this->peek_token;
	this->peek_token = this->lexer.get_token();
}

void Parser::program()
{
	std::cout << "{SNITCH PROGRAM}" << std::endl;

	while(check(this->lexer.TOKEN_NEWLINE))
		advance();

	while(!check(this->lexer.TOKEN_EOF))
		statement();
}

void Parser::statement()
{
	if(this->current_token.type == this->lexer.TOKEN_COMMENT)
	{
		std::cout << "{COMMENT STATEMENT}" << std::endl;
		advance();
	}


	newline();
}

void Parser::newline()
{
	std::cout << "{COMMENT STATEMENT git OVER}" << std::endl;

	while(check(this->lexer.TOKEN_NEWLINE))	
		advance();
}


parser.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#pragma once

#include <iostream>
#include "token.h"
#include "lexer.h"

class Parser 
{
private:
	Lexer lexer;
	Token current_token;
	Token peek_token;
public:
	Parser() = default;
	Parser(Lexer lexer);
	bool check(int token_numb);
	bool check_peek(int token_numb);
	void advance();
	void program();
	void statement();
	void newline();
};


If someone could help me out, that would be great.
Thanks!
The error message includes this line:
lexer.h:9:7: error: use of deleted function ‘std::__cxx11::basic_stringstream<_CharT,
 _Traits, _Alloc>::basic_stringstream(const std::__cxx11::basic_stringstream<_CharT, _Traits,
 _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
It says the copy constructor for std::stringstream is "deleted" (disabled) and can't be used, but your code tries to use it anyway.
https://en.cppreference.com/w/cpp/language/function#Deleted_functions

The first few lines of the error tell you where that's happening and why:
snitch.cpp: In function ‘int main(int, char**)’:
snitch.cpp:21:37: error: use of deleted function ‘Lexer::Lexer(const Lexer&)’
   21 |         Parser parser = Parser(lexer);
      |                                     ^
In file included from snitch.cpp:3:
lexer.h:9:7: note: ‘Lexer::Lexer(const Lexer&)’ is implicitly deleted because the
default definition would be ill-formed:
It means you're trying to copy a Lexer in line 21 of the main function, but that's not possible because it would require the compiler to copy the stringstream contained in the Lexer as a data member.

Fortunately, it appears that the std::stringstream doesn't need to be a data member of Lexer at all, so you can avoid the problem by making it a local variable in Lexer::get_token instead.

There are plenty of options if you can't avoid the problem after all.
Last edited on
Thank you mbozzi!
In get_token() L92 and L97, shouldn't the bool condition for is_comment be reversed - ie use:

 
else if(this->curr_char == ':' && !this->is_comment)


so that a colon is only parsed as TOKEN_COLON if not within a comment. Same for L97.

It's good practice to restrict the scope of a variable to the smallest possible. So temp can be further restricted to specific blocks that use it. And then clear_stream() isn't then required.

See C++ Core Guidelines ES.5 ES.21
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines

PS. Why use a std::stringstream for temp and not just a std::string?

Last edited on
Topic archived. No new replies allowed.