Question

I have a complex class type and I want to define a default constant instance that can be copied an used as a parameter to a function. I also want to use that instance as a default parameter so the function can be called without it. The function expects a const rvalue reference.

In the following code, the disadvantage of function test1 is that the code for the definition of the default value is duplicated.

Is the function test2 valid? Will the defaultS be left in unspecified state after the first call? In that case, is there a better way to avoid code duplication?

The code prints 2 1 1 as expected when compiled with or without optimization (gcc 4.8). However, can it be relied on to work with any compiler (that supports c++11)?

#include <iostream>
#include <utility>

struct S {
    // Imagine that this is a very complex class.
    int data1, data2, data3;
};

// Duplicated code for defining default parameter and the constexpr.
// This example is trivial, but remember that S is more complex in a real use case.
constexpr S defaultS = {0, 0, 1};
void test1(const S&& arg = S{0, 0, 1}) {
    std::cout << arg.data1 + arg.data2 + arg.data3 << std::endl;
}

// Default values are defined only once when defining defaultS.
// Will defaultS be unspecified after first call?
void test2(const S&& arg = std::move(defaultS)) {
    std::cout << arg.data1 + arg.data2 + arg.data3 << std::endl;
}

int main(int argc, char **argv) {
    // Use case for defaultS.
    // Most values are default but a few are changed.
    auto param = defaultS;
    param.data3 = 2;
    test1(std::move(param));

    test2(); // defaultS is moved from.
    test2(); // defaultS is used again here. Is it unspecified?
             // and will there therefore be undefined behaviour?

    return 0;
}
Was it helpful?

Solution

Since std::move(defaultS) deduces the argument type as S const& and returns a typename std::remove_reference<S const&>::type&&, i.e., a S const&& I'd think the code is OK: you won't be able to move from the S const&& as the object is const.

It doesn't make much sense to take a S const&& as argument, I'd think. When you want to take a S&& as argument you would need to create an object, e.g. using

void foo(S&& value = S(defaultS)) { ... }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top