0

Please consider the following (simple) code. The strange (?) behavior is in the main() routine and detailed below.

data class

Packet.h

#include <string>

class Packet {
public:
    Packet(int iParam, std::string sParam);
    ~Packet();
    void setInt(int iParam);
    void setString(std::string sParam);

private:
    int iProperty_;
    std::string sProperty_;
};

Packet.cpp

#include "Packet.h"
using std::string;

Packet::Packet(int iParam, string sParam) : iProperty_(iParam), 
    sProperty_(sParam) {}
Packet::~Packet() {}

void Packet::setInt(int iParam) {
    iProperty_ = iParam;
}
void Packet::setString(std::string sParam) {
    sProperty_ = sParam;
}

controller class

PacketController.h

#include <string>

class PacketController {
public:
    PacketController();
    ~PacketController();
    PacketController & andSetInt(int iParam);
    PacketController & andSetString(std::string sParam);

private:
    Packet packet_;
};

PacketController.cpp

#include "PacketController.h"
using std::string;

PacketController::PacketController() : packet_(0, "") {}
PacketController::~PacketController() {}

PacketController & PacketController::andSetInt(int iParam) {
    packet_.setInt(iParam);
    return *this;
}

PacketController & PacketController::andSetString(string sParam) {
    packet_.setString(sParam);
    return *this;
}

main()

int main() {
    PacketController& ctrlRef = PacketController()
        .andSetString("hello world")
        .andSetInt(19);

    PacketController ctrlVal = PacketController()
        .andSetString("hello world")
        .andSetInt(19);

    PacketController& ctrlRef2 = PacketController();
    ctrlRef2.andSetString("hello world")
        .andSetInt(19);

    return 0;
}

If execution is paused at the line return 0; of main() then the following values are seen on the internal packet_ objects:

ctrlRef - packet_: iProperty_: 19 sProperty_: ""

ctrlVal - packet_: iProperty_: 19 sProperty_: "hello world"

ctrlRef2 - packet_: iProperty_: 19 sProperty_: "hello world"

So why is sProperty_ empty on the packet_ object in ctrlRef? Is it something to do with the reference being seated on initialisation of the PacketController object? But then why is iProperty_ on the packet_ object of ctrlRef correctly set to 19?

  • ymmv, but i think the named parameter idiom brings loads of boilerplate for little gain, you can have the names of the parameters appear on the call site if you simply use a `struct params { /*all params that you want to pass (with their names) */}` and make the constructor take a `params` – 463035818_is_not_an_ai Oct 16 '18 at 12:06
  • Your compiler should warn you about the code you show. If not then enable more warnings. – Some programmer dude Oct 16 '18 at 12:18
  • @1: had to look up 'ymmv' :) some example code? client code above is cool –  Oct 16 '18 at 12:29
  • @2: no warnings by default on Visual Studio and runtime execution doesn't appear to have any problem... (other than missing string) –  Oct 16 '18 at 12:30

1 Answers1

0
PacketController& ctrlRef = PacketController()
    .andSetString("hello world")
    .andSetInt(19);

ctrlRef is a reference to a temporary whose lifetime ended at the end of the full-expression evaluation (right at the ;). The same can be said about ctrlRef2.

Using it results in undefined behavior.

On the other hand, ctrlVal is a value initialized from the temporary. Using it is fine.

YSC
  • 38,212
  • 9
  • 96
  • 149
  • [There are ways](https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/)... but resorting to a `std::unique` is probably best. – YSC Oct 16 '18 at 12:06
  • Thanks - useful link to GotW above. Worth noting that a reference to `const` can be bound to a temporary - although not much use above... –  Oct 16 '18 at 12:21