Home > Software design >  How to avoid new when "error: no viable overloaded '='"?
How to avoid new when "error: no viable overloaded '='"?

Time:07-31

I think I have a fundamental misunderstand of how objects are being constructed.

I have some code that is currently constructing objects using new which I've been told is bad practice. So I figured I could take the new keyword out, replace my pointer with actual objects, and remove the delete statements. Unfortunately, when I remove the new statement, I get error: no viable overloaded '='. I don't understand why.

The object I'm trying to make is a LightWeightNeuralNetwork; see https://github.com/lwtnn/lwtnn/blob/master/include/lwtnn/LightweightNeuralNetwork.hh

Edit: it has been pointed out in the comments by UnholySheep that LightweightNeuralNetwork has no default constructor, and this is the source of the problem (when the class TNeuralNetworkLWTNN is initialised, it somehow needs default constructors for all member objects?). Unfortunately, the solution in the question here proposed as a dupe will not work, as that solution requires the problem object is created in the initializer list. LightweightNeuralNetwork needs a file to be opened before it can be created.

My code is normally compiled as part of a larger framework, but here is a toy model that demonstrates the relevant bits.

A header file;

#ifndef VNEURALNETWORKLWTNN_H
#define VNEURALNETWORKLWTNN_H

#include <iostream>

// Becuase we have a field of type LightweightNeuralNetwork
#include "lwtnn/LightweightNeuralNetwork.hh"

class TNeuralNetworkLWTNN {
public:
  // same as for lwtnn
  typedef std::map<std::string, double> NetworkInputs;
  typedef std::map<std::string, double> NetworkOutputs;

  // constructor and destructor
  explicit TNeuralNetworkLWTNN(std::string inputFile);
  ~TNeuralNetworkLWTNN();

  // business end
  NetworkOutputs compute(NetworkInputs inputs);

private:
  // !!Change this to not be a pointer!!
  lwt::LightweightNeuralNetwork * lwtnn_neural;
};

#endif

and a class;

#include "TNeuralNetworkLWTNN.h"
#include <fstream>
#include <sstream>

// LWTNN
#include "lwtnn/LightweightNeuralNetwork.hh"
#include "lwtnn/parse_json.hh"

TNeuralNetworkLWTNN::TNeuralNetworkLWTNN(std::string inputFile) {
  // The input file is read into a stringstream
  std::ifstream input(inputFile);
  std::stringstream sin;
  sin << input.rdbuf();
  input.close();
  // build the graph
  lwt::JSONConfig config = lwt::parse_json(sin);
  // !! I don't want to be using new !!
  // !! Change this to just use '=' !!
  // !! error: no viable overloaded '=' !!
  lwtnn_neural = new lwt::LightweightNeuralNetwork(config.inputs, config.layers,
                                                   config.outputs);
};

// I'd rather not need any code here
TNeuralNetworkLWTNN::~TNeuralNetworkLWTNN(){
  delete lwtnn_neural;
};

TNeuralNetworkLWTNN::NetworkOutputs
TNeuralNetworkLWTNN::compute(TNeuralNetworkLWTNN::NetworkInputs inputs) {
  // !! Change this to use . not -> !!
  TNeuralNetworkLWTNN::NetworkOutputs outputs =
      TNeuralNetworkLWTNN::lwtnn_neural->compute(inputs);
  return outputs;
};

Apologies for not producing something you can compile, I don't have access to the library lwtnn outside the framework I'm writing in. I'm hopeful that my mistake is quite basic.

Complete error message (with some paths modified);

/path/to/repo/TNeuralNetworkLWTNN.cxx:9:22: error: constructor for 'TNeuralNetworkLWTNN' must explicitly initialize the member 'lwtnn_neural' which does not have a default constructor
TNeuralNetworkLWTNN::TNeuralNetworkLWTNN(std::string inputFile)
                     ^
/path/to/repo/TNeuralNetworkLWTNN.h:36:34: note: member is declared here
  lwt::LightweightNeuralNetwork  lwtnn_neural;
                                 ^
/path/to/library/lwtnn/LightweightNeuralNetwork.hh:41:9: note: 'lwt::LightweightNeuralNetwork' declared here
  class LightweightNeuralNetwork
        ^
/path/to/repo/TNeuralNetworkLWTNN.cxx:18:16: error: no viable overloaded '='
  lwtnn_neural = lwt::LightweightNeuralNetwork(config.inputs, config.layers,
  ~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/path/to/releases/lwtnn/2.11.1-2ee64/x86_64-centos7-gcc11-opt/include/lwtnn/LightweightNeuralNetwork.hh:52:31: note: candidate function not viable: expects an lvalue for 1st argument
    LightweightNeuralNetwork& operator=(LightweightNeuralNetwork&) = delete;
                              ^
2 errors generated.
make[2]: *** [CMakeFiles/GenericTest.dir/TNeuralNetworkLWTNN.cxx.o] Error 1
make[2]: *** Waiting for unfinished jobs....
make[1]: *** [CMakeFiles/GenericTest.dir/all] Error 2
make: *** [all] Error 2

CodePudding user response:

The problem is that lwt::LightweightNeuralNetwork is not moveable, which, as @ n. 1.8e9-where's-my-share m. pointed out, seems to be a bug in the library, as there doesn't appear to be any reason for it not to be.

If you don't want to or for some reason cannot make changes to the library yourself (and waiting for it to be fixed is an unacceptable delay) then you can simply replace lwt::LightweightNeuralNetwork * lwtnn_neural; with std::unique_ptr<lwt::LightweightNeuralNetwork> lwtnn_neural; and in the constructor replace lwtnn_neural = new lwt::LightweightNeuralNetwork(config.inputs, config.layers, config.outputs); with lwtnn_neural = std::make_unique<lwt::LightweightNeuralNetwork>(config.inputs, config.layers, config.outputs);


However adding move construction and assignment is pretty trivial, so you can consider doing that yourself, e.g.:

LightweightNeuralNetwork(LightweightNeuralNetwork&& other) noexcept : 
  m_impl(std::move(other.m_impl) {}

LightweightNeuralNetwork& operator=(LightweightNeuralNetwork&& other) noexcept {
  if (this != &other) {
    m_impl = std::move(other.m_impl);
  }
  return *this;
}

And then you can have a non-pointer member in combination with an extra function to initialize the lwt::LightweightNeuralNetwork in a member initializer list, e.g.:

// Moved all the logic from the constructor body into separate function
static lwt::LightweightNeuralNetwork createNetwork(const std::string& inputFile) {
  // The input file is read into a stringstream
  std::ifstream input(inputFile);
  std::stringstream sin;
  sin << input.rdbuf();
  input.close();
  // build the graph
  lwt::JSONConfig config = lwt::parse_json(sin);
  return lwt::LightweightNeuralNetwork(config.inputs, config.layers, config.outputs);
}

TNeuralNetworkLWTNN::TNeuralNetworkLWTNN(std::string inputFile) : 
  lwtnn_neural(createNetwork(inputFile)) // function used to create object for member
  {}
  • Related