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
{}