2

Assume I have a class with different constructors:

class A
{
public:
    A(char* string)
    {
        //...
    }

    A(int value)
    {
        //..
    }

    void check() {}
};

Now I want to create an A object on stack, the constructor must be choosed depending on some condition, but there is a problem: the created object is destroyed then we quit {...} block.

bool isTrue() { /*...*/ }

int main() 
{
    if (isTrue())
    {
        A a("string");
    }
    else
    {
        A a(10);
    }
    a.check(); //error: 'a' is not defined in this scope
}

Suppose I haven't the copy-constructor or operator= in the A class. So how can solve this issue? http://ideone.com/YsjmnK

yakov
  • 444
  • 3
  • 14

5 Answers5

6

A a = isTrue() ? A("string") : A(10);

And if a.check() is a const member function, an alternative may be better:

const A& a = isTrue() ? A("string") : A(10);

The object will be destroyed when the reference a go out of scope.

Note since C++17, according to the rule of copy elision the copy/move constructor is not required to be accessible for this case; copy elision is guaranteed here.

And since C++17 you can use std::optional, which doesn't cause any dynamic memory allocation. e.g.

std::optional<A> a;
if (isTrue())
{
    a.emplace("string");
}
else
{
    a.emplace(10);
}
(*a).check();

BTW: A(char* string) is supposed to be A(const char* string).

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • It works and it seems there are no pitfalls. Thanks! :) – yakov Feb 27 '14 at 08:54
  • 1
    @yakov I'm not sure very much that the copy ctor and `operator=()` will not be called on every compilers. But as least it works well on my compiler(g++). Maybe you need to do something to confirm it. – songyuanyao Feb 27 '14 at 09:00
  • 1
    This definitely requires an accessible copy constructor (although the compiler will probably optimize it out of the generated code). – James Kanze Feb 27 '14 at 09:09
  • 1
    I've added the definitions of copy ctor and assignment operator into my example: http://ideone.com/YsjmnK and I see that they are not called then we use the ternary operator. I think it's an enough confirmation, could you please correct me if I'm wrong? – yakov Feb 27 '14 at 09:20
  • @yakov I think it's OK. You confirmed it in the same way with mine. – songyuanyao Feb 27 '14 at 09:27
  • @JamesKanze Do you mean that the copy ctor should be called in this case according to the stardards? Yes, I think the compiler will optimize it in most case, but I'm not sure. – songyuanyao Feb 27 '14 at 10:03
  • 3
    @yakov You're wrong:-). This is one of those cases where the standard requires an accessible copy constructor, but allows the compiler to elide it. Try making the copy constructor private, for example. The standard is very explicit about this: "The second and third operands have the same type; the result is of that type. If the operands have class type, the result is a prvalue temporary of the result type, which is **copy-initialized** from either the second operand or the third operand depending on the value of the first operand. – James Kanze Feb 27 '14 at 12:03
  • @yakov It doesn't compile for me if there is no accessible copy constructor. At least not with Microsoft. (g++ seems to be broken here.) – James Kanze Feb 27 '14 at 12:04
  • @JamesKanze Confirmed, `isTrue() ? A("string") : A(10)` requires an accessible copy constructor in Microsoft Visual Studio 2012. – fredoverflow Feb 27 '14 at 12:25
  • @FredOverflow And _not_ in g++. For once, it's MS who is closer to the standard. – James Kanze Feb 27 '14 at 12:31
  • Ok guys, I see that ideone.com tried to cheat me. The copy ctor `A(const A& ref) { std::cout << "A(const A& ref) called" << std::endl; }` was called then we use ternary operator, but there was not std output. (the same http://ideone.com/YsjmnK) But if I move this ctor to the private section, this code will note be compiled. And if I compile and run it by MVS2012, all the expected outputs present. – yakov Feb 27 '14 at 12:51
  • I've tried with a private copy ctor on g++ and clang, and for `A a = isTrue() ? A("string") : A(10);`, g++ and clang could not compile. For `const A& a = ...` and `A&& a = ...`, g++ compiled OK, clang could not. – songyuanyao Feb 28 '14 at 03:12
  • What is the second example supposed to do? It looks like a reference to an rvalue, unless I'm missing something. – John P Feb 17 '17 at 02:43
  • 1
    @JohnP Yes, a temporary is bound to an lvalue reference to const. See [this](http://en.cppreference.com/w/cpp/language/reference_initialization#Lifetime_of_a_temporary) for more details. – songyuanyao Feb 17 '17 at 03:46
3

You can't satisfy all your stated requirements.

If you can get rid of the requirement for the object to be on stack, you could use a pointer.

A *a;
if (isTrue())
    a = new A("string");
else
    a = new A(10);
a->check();
delete a;
Janis Kirsteins
  • 2,128
  • 16
  • 17
3

If the type has a default constructor, you can default-construct an object, immediately destruct it, and then construct it again with the appropriate constructor via placement-new:

A a;
a.~A();
if (isTrue())
{
    new(&a) A("string");
}
else
{
    new(&a) A(10);
}

The C++ standard has several examples similar to the above, just search for .~ and ->~.

Note that this is ultra evil. If your code ever gets reviewed, you are probably going to get fired.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • On C++11 it's a good idea. And if `a.check()` is a const function, `const A& a = isTrue() ? A("string") : A(10);` may be a better solution whick can work with c++98. – songyuanyao Feb 27 '14 at 10:20
  • @songyuanyao If all he needs to do with the object is call the `check` function, then he can simply write `(isTrue() ? A("string") : A(10)).check();` but I doubt that's what he actually wants. – fredoverflow Feb 27 '14 at 11:30
  • It still requires an accessible copy constructor. (At least according to the standard. g++ doesn't enforce this, but other compilers, including Microsoft, do.) – James Kanze Feb 27 '14 at 11:58
1

I had the exact same question a while ago and this is what google helped me find:

unique_ptr<A> foo;

if(isTrue())
    foo = std::unique_ptr<A>(new A("10"));
else
    foo = std::unique_ptr<A>(new A(10));

Its probably too late for the OP but someone else might hopefully find this useful.

Kulki
  • 83
  • 7
  • It still doesn't satisfy "on the stack" but I'd suspect OP is a bit unclear on what his own requirements are in that respect. – M.M Apr 11 '14 at 12:56
-2

You can use the template class:

template<class type> class A
{
protected:
    type    T;
public:

    void A(type t_curr) {T = t_curr;};//e.g.---

    void check() {}
};
gazza
  • 24
  • 4