C++ and ExprTk parser "use of deleted function" error

426 views Asked by At

I'm trying to use the ExprTk mathematical expression parser library within a class whose objects are to be stored in a vector of objects, which is a member variable of another class; however, when I try to push_back a new object in the vector I'm getting a lot of "use of deleted function" errors. Here is a simple version of the code that is giving me problems:

#include <exprtk.hpp>
#include <iostream>
#include <string>
#include <vector>

class B {
public:
  double x;
  exprtk::symbol_table<double> symbol_table;
  exprtk::parser<double> parser;
  exprtk::expression<double> expr_obj;

  B();
};

class A {
public:

  std::vector<B> Bvec;

  A();
};

A::A() {
  Bvec.push_back(B());
};

B::B() {
  symbol_table.add_variable("x", x);
  expr_obj.register_symbol_table(symbol_table);
  parser.compile("x^2",expr_obj);

  x = 2.0;
  std::cout << expr_obj.value() << std::endl;

}

int main(int argc, char const* argv[]) {

  A a_obj;

  return 0;
}

I haven't included the header library since it's nearly 40,000 lines, but it can be found here: http://www.partow.net/programming/exprtk/.

Here is the error message

In file included from /usr/include/x86_64-linux-gnu/c++/7/bits/c++allocator.h:33:0,
                 from /usr/include/c++/7/bits/allocator.h:46,
                 from /usr/include/c++/7/string:41,
                 from /usr/include/c++/7/bits/locale_classes.h:40,
                 from /usr/include/c++/7/bits/ios_base.h:41,
                 from /usr/include/c++/7/ios:42,
                 from /usr/include/c++/7/ostream:38,
                 from /usr/include/c++/7/iostream:39,
                 from src/main.cpp:1:
/usr/include/c++/7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = B; _Args = {B}; _Tp = B]’:
/usr/include/c++/7/bits/alloc_traits.h:475:4:   required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = B; _Args = {B}; _Tp = B; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<B>]’
/usr/include/c++/7/bits/vector.tcc:100:30:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {B}; _Tp = B; _Alloc = std::allocator<B>]’
/usr/include/c++/7/bits/stl_vector.h:954:21:   required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = B; _Alloc = std::allocator<B>; std::vector<_Tp, _Alloc>::value_type = B]’
src/main.cpp:25:21:   required from here
/usr/include/c++/7/ext/new_allocator.h:136:4: error: use of deleted function ‘B::B(B&&)’
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.cpp:6:7: note: ‘B::B(B&&)’ is implicitly deleted because the default definition would be ill-formed:
 class B {
       ^
src/main.cpp:6:7: error: ‘exprtk::parser<T>::parser(const exprtk::parser<T>&) [with T = double]’ is private within this context
In file included from src/main.cpp:3:0:
ext_libs/exprtk/exprtk.hpp:35289:7: note: declared private here
       parser(const parser<T>&);
       ^~~~~~
In file included from /usr/include/c++/7/bits/stl_tempbuf.h:60:0,
                 from /usr/include/c++/7/bits/stl_algo.h:62,
                 from /usr/include/c++/7/algorithm:62,
                 from ext_libs/exprtk/exprtk.hpp:37,
                 from src/main.cpp:3:
/usr/include/c++/7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = B; _Args = {B}]’:
/usr/include/c++/7/bits/stl_uninitialized.h:83:18:   required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<B*>; _ForwardIterator = B*; bool _TrivialValueTypes = false]’
/usr/include/c++/7/bits/stl_uninitialized.h:134:15:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<B*>; _ForwardIterator = B*]’
/usr/include/c++/7/bits/stl_uninitialized.h:289:37:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<B*>; _ForwardIterator = B*; _Tp = B]’
/usr/include/c++/7/bits/stl_uninitialized.h:311:2:   required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = B*; _ForwardIterator = B*; _Allocator = std::allocator<B>]’
/usr/include/c++/7/bits/vector.tcc:426:6:   required from ‘void std::vector<_Tp, _Alloc>::_M_realloc_insert(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {B}; _Tp = B; _Alloc = std::allocator<B>; std::vector<_Tp, _Alloc>::iterator = __gnu_cxx::__normal_iterator<B*, std::vector<B> >; typename std::_Vector_base<_Tp, _Alloc>::pointer = B*]’
/usr/include/c++/7/bits/vector.tcc:105:21:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {B}; _Tp = B; _Alloc = std::allocator<B>]’
/usr/include/c++/7/bits/stl_vector.h:954:21:   required from ‘void std::vector<_Tp, _Alloc>::push_back(std::vector<_Tp, _Alloc>::value_type&&) [with _Tp = B; _Alloc = std::allocator<B>; std::vector<_Tp, _Alloc>::value_type = B]’
src/main.cpp:25:21:   required from here
/usr/include/c++/7/bits/stl_construct.h:75:7: error: use of deleted function ‘B::B(B&&)’
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Makefile:29: recipe for target 'obj/src/main.o' failed
make: *** [obj/src/main.o] Error 1

I'm pretty sure the issue has something to do with the push_back function and how the object is being copied; however, that's a bit over my head and knowledge of c++ (which is rather basic).

Any help would be appreciated. Thanks!

2

There are 2 answers

0
user4581301 On

parser cannot be copied, and based on the signature of how copying was prevented, the copy constructor was made private, it's unlikely that it can be moved either (private copy constructers were what you used before the disabling of a special member function with the delete keyword was added to the language in C++11, the same time move semantics were added). You cannot have a exprtk::parser instance as a member of an object you want to copy (unless you're going to get really weird in custom special member functions and NOT copy the exprtk::parser).

This is all enforcement of you not wanting to have multiple instance of the same Parser floating around. You will have to use a reference, most likely a smart pointer because references are a <expletive deleted> to copy assign, to a single instance instead.

But this raises the question of whether you need to keep parser around as a member at all. What about something like this:

class B {
public:
  double x; // Not sure we need even this.
  double result;
  B();
};

B::B():
{
  // parser is handled with local variables.
  exprtk::symbol_table<double> symbol_table;
  exprtk::parser<double> parser;
  exprtk::expression<double> expr_obj;

  symbol_table.add_variable("x", x);
  expr_obj.register_symbol_table(symbol_table);
  parser.compile("x^2",expr_obj);

  x = 2.0;
  result = expr_obj.value(); // store instead of printing
} // parser and friends are no longer needed and discarded.
0
ExprTk Developer On

Looking at the ExprTk documentation (readme.txt), specifically Section 10.3 we have the following note:

Note:  The  exprtk::parser  is  a  non-copyable  and  non-thread  safe
component, and should only be shared via either a reference, a  shared
pointer  or  a  std::ref  mechanism,  and  considerations  relating to
synchronisation  taken  into  account  where  appropriate.  The parser
represents an object factory,  specifically a factory of  expressions,
and generally should  not be instantiated  solely on a  per expression
compilation basis.

Section 10.3